comfyui-frontend-package 1.38.5__py3-none-any.whl → 1.38.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js → AboutPanel-lkjGFasi.js} +2 -2
  2. comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js.map → AboutPanel-lkjGFasi.js.map} +1 -1
  3. comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js → AudioPreviewPlayer-BxCSKPl9.js} +2 -2
  4. comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js.map → AudioPreviewPlayer-BxCSKPl9.js.map} +1 -1
  5. comfyui_frontend_package/static/assets/AudioPreviewPlayer-CkxKvcVf.js +1 -0
  6. comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js → BaseViewTemplate-CjODF2hh.js} +2 -2
  7. comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js.map → BaseViewTemplate-CjODF2hh.js.map} +1 -1
  8. comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js → CloudAuthTimeoutView-D-QkjPNh.js} +2 -2
  9. comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js.map → CloudAuthTimeoutView-D-QkjPNh.js.map} +1 -1
  10. comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js → CloudBadge-B4nmLus2.js} +2 -2
  11. comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js.map → CloudBadge-B4nmLus2.js.map} +1 -1
  12. comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js → CloudForgotPasswordView-DOEV9hGr.js} +2 -2
  13. comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js.map → CloudForgotPasswordView-DOEV9hGr.js.map} +1 -1
  14. comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js → CloudLayoutView-ShKH6rRV.js} +2 -2
  15. comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js.map → CloudLayoutView-ShKH6rRV.js.map} +1 -1
  16. comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js → CloudLoginView-C3Te42U9.js} +2 -2
  17. comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js.map → CloudLoginView-C3Te42U9.js.map} +1 -1
  18. comfyui_frontend_package/static/assets/CloudRunButtonWrapper-Cub7EB34.js +3 -0
  19. comfyui_frontend_package/static/assets/{CloudRunButtonWrapper-hQc4BNkX.js.map → CloudRunButtonWrapper-Cub7EB34.js.map} +1 -1
  20. comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js → CloudSignupView-X2oiL3ZR.js} +2 -2
  21. comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js.map → CloudSignupView-X2oiL3ZR.js.map} +1 -1
  22. comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js → CloudSubscriptionRedirectView-UjNv8emo.js} +2 -2
  23. comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js.map → CloudSubscriptionRedirectView-UjNv8emo.js.map} +1 -1
  24. comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js → CloudSurveyView-IaiucCTP.js} +2 -2
  25. comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js.map → CloudSurveyView-IaiucCTP.js.map} +1 -1
  26. comfyui_frontend_package/static/assets/ComfyQueueButton-BppnHbrl.js +1 -0
  27. comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js → ComfyQueueButton-HjSIKZKO.js} +2 -2
  28. comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js.map → ComfyQueueButton-HjSIKZKO.js.map} +1 -1
  29. comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js → ExtensionPanel-Bzb9QtKj.js} +2 -2
  30. comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js.map → ExtensionPanel-Bzb9QtKj.js.map} +1 -1
  31. comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js → GlobalToast-BSCvu6Hw.js} +2 -2
  32. comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js.map → GlobalToast-BSCvu6Hw.js.map} +1 -1
  33. comfyui_frontend_package/static/assets/{GraphView-BCkpNGgz.js → GraphView-gYVCtm1V.js} +5 -5
  34. comfyui_frontend_package/static/assets/GraphView-gYVCtm1V.js.map +1 -0
  35. comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js → KeybindingPanel-DF-bG4iO.js} +2 -2
  36. comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js.map → KeybindingPanel-DF-bG4iO.js.map} +1 -1
  37. comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js → LegacyCreditsPanel-D-CboO8k.js} +2 -2
  38. comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js.map → LegacyCreditsPanel-D-CboO8k.js.map} +1 -1
  39. comfyui_frontend_package/static/assets/Load3D-c9UwgGoI.js +1 -0
  40. comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js → Load3D-vYr8M3jJ.js} +2 -2
  41. comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js.map → Load3D-vYr8M3jJ.js.map} +1 -1
  42. comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js → PanelTemplate-BJda9e5J.js} +2 -2
  43. comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js.map → PanelTemplate-BJda9e5J.js.map} +1 -1
  44. comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js → ServerConfigPanel-VsC6xlZJ.js} +2 -2
  45. comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js.map → ServerConfigPanel-VsC6xlZJ.js.map} +1 -1
  46. comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js → SubscribeButton-DZBycfCA.js} +2 -2
  47. comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js.map → SubscribeButton-DZBycfCA.js.map} +1 -1
  48. comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js → SubscribeToRun-4YolxBOL.js} +2 -2
  49. comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js.map → SubscribeToRun-4YolxBOL.js.map} +1 -1
  50. comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js → SubscriptionPanel-B6txX4Vm.js} +2 -2
  51. comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js.map → SubscriptionPanel-B6txX4Vm.js.map} +1 -1
  52. comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js → SubscriptionRequiredDialogContent-COEF2VQ_.js} +2 -2
  53. comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js.map → SubscriptionRequiredDialogContent-COEF2VQ_.js.map} +1 -1
  54. comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js → UserCheckView-x-fkcYzc.js} +2 -2
  55. comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js.map → UserCheckView-x-fkcYzc.js.map} +1 -1
  56. comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js → UserPanel-7N9QknQj.js} +2 -2
  57. comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js.map → UserPanel-7N9QknQj.js.map} +1 -1
  58. comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js → UserSelectView-BYjOkfSa.js} +2 -2
  59. comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js.map → UserSelectView-BYjOkfSa.js.map} +1 -1
  60. comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js → ValueControlPopover-BPAa35QG.js} +2 -2
  61. comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js.map → ValueControlPopover-BPAa35QG.js.map} +1 -1
  62. comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js → WidgetAudioUI-Dw-r3Ews.js} +2 -2
  63. comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js.map → WidgetAudioUI-Dw-r3Ews.js.map} +1 -1
  64. comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js → WidgetImageCrop-kERy9g5I.js} +2 -2
  65. comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js.map → WidgetImageCrop-kERy9g5I.js.map} +1 -1
  66. comfyui_frontend_package/static/assets/{WidgetInputNumber-BOKO36G3.js → WidgetInputNumber-BaClCNAC.js} +1 -1
  67. comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js +3 -0
  68. comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js.map +1 -0
  69. comfyui_frontend_package/static/assets/{WidgetLegacy-Bslv9wZZ.js → WidgetLegacy-B4nipUM9.js} +1 -1
  70. comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js → WidgetRecordAudio-Nk8dH238.js} +2 -2
  71. comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js.map → WidgetRecordAudio-Nk8dH238.js.map} +1 -1
  72. comfyui_frontend_package/static/assets/WidgetSelect-DzZPpO_-.js +1 -0
  73. comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js → WidgetSelect-nSQrk_hd.js} +2 -2
  74. comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js.map → WidgetSelect-nSQrk_hd.js.map} +1 -1
  75. comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js → WidgetWithControl-Da6zUB5e.js} +3 -3
  76. comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js.map → WidgetWithControl-Da6zUB5e.js.map} +1 -1
  77. comfyui_frontend_package/static/assets/{api-CUAc7rDA.js → api-Dwq2LQIW.js} +4 -4
  78. comfyui_frontend_package/static/assets/api-Dwq2LQIW.js.map +1 -0
  79. comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js → audioService-DvVaKhuU.js} +2 -2
  80. comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js.map → audioService-DvVaKhuU.js.map} +1 -1
  81. comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js → audioUtils-DD4rUYVZ.js} +2 -2
  82. comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js.map → audioUtils-DD4rUYVZ.js.map} +1 -1
  83. comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js → auth-B9axG-yZ.js} +2 -2
  84. comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js.map → auth-B9axG-yZ.js.map} +1 -1
  85. comfyui_frontend_package/static/assets/auth-D74DTev8.js +1 -0
  86. comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js → cloudBadges-D5mGJbRy.js} +2 -2
  87. comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js.map → cloudBadges-D5mGJbRy.js.map} +1 -1
  88. comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js → cloudFeedbackTopbarButton-RZUssOmb.js} +2 -2
  89. comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js.map → cloudFeedbackTopbarButton-RZUssOmb.js.map} +1 -1
  90. comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js → cloudRemoteConfig-F9J0iGyF.js} +2 -2
  91. comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js.map → cloudRemoteConfig-F9J0iGyF.js.map} +1 -1
  92. comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js → cloudSessionCookie-MEORlqtg.js} +2 -2
  93. comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js.map → cloudSessionCookie-MEORlqtg.js.map} +1 -1
  94. comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js → cloudSubscription-CuWNXKVx.js} +2 -2
  95. comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js.map → cloudSubscription-CuWNXKVx.js.map} +1 -1
  96. comfyui_frontend_package/static/assets/{core-DBfeqMDR.js → core-IYu8XAIx.js} +4 -4
  97. comfyui_frontend_package/static/assets/{core-DBfeqMDR.js.map → core-IYu8XAIx.js.map} +1 -1
  98. comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js → dialogService-YG0RH337.js} +9 -9
  99. comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js.map → dialogService-YG0RH337.js.map} +1 -1
  100. comfyui_frontend_package/static/assets/firebaseAuthStore-DnNaPbuZ.js +1 -0
  101. comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js → graphHasMissingNodes-BhD1N6zI.js} +2 -2
  102. comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js.map → graphHasMissingNodes-BhD1N6zI.js.map} +1 -1
  103. comfyui_frontend_package/static/assets/{index-CGxJFSof.js → index-BMy3twho.js} +3 -3
  104. comfyui_frontend_package/static/assets/{index-CGxJFSof.js.map → index-BMy3twho.js.map} +1 -1
  105. comfyui_frontend_package/static/assets/{index-DNpOhRra.css → index-KMO9qFHH.css} +1 -1
  106. comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js → keybindingService-CBLPjYHI.js} +2 -2
  107. comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js.map → keybindingService-CBLPjYHI.js.map} +1 -1
  108. comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js → releaseStore-DDOxzkVb.js} +2 -2
  109. comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js.map → releaseStore-DDOxzkVb.js.map} +1 -1
  110. comfyui_frontend_package/static/assets/releaseStore-iVkqunL8.js +1 -0
  111. comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js → subscriptionCheckoutUtil-DswSOreM.js} +2 -2
  112. comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js.map → subscriptionCheckoutUtil-DswSOreM.js.map} +1 -1
  113. comfyui_frontend_package/static/assets/{useCurrentUser-BJcn2Vgo.js → useCurrentUser-NdaCJzIK.js} +1 -1
  114. comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js → useErrorHandling-Cfa5N_7c.js} +2 -2
  115. comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js.map → useErrorHandling-Cfa5N_7c.js.map} +1 -1
  116. comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js → useSubscriptionDialog-792qfEJ2.js} +3 -3
  117. comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js.map → useSubscriptionDialog-792qfEJ2.js.map} +1 -1
  118. comfyui_frontend_package/static/assets/useSubscriptionDialog-B-eGeK3j.js +1 -0
  119. comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js → userStore-BAS9m9W6.js} +2 -2
  120. comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js.map → userStore-BAS9m9W6.js.map} +1 -1
  121. comfyui_frontend_package/static/assets/vendor-three-BFcUNSs9.js.map +1 -1
  122. comfyui_frontend_package/static/index.html +1 -1
  123. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/METADATA +1 -1
  124. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/RECORD +126 -126
  125. comfyui_frontend_package/static/assets/AudioPreviewPlayer-BoEdyGI_.js +0 -1
  126. comfyui_frontend_package/static/assets/CloudRunButtonWrapper-hQc4BNkX.js +0 -3
  127. comfyui_frontend_package/static/assets/ComfyQueueButton-BbQnRThI.js +0 -1
  128. comfyui_frontend_package/static/assets/GraphView-BCkpNGgz.js.map +0 -1
  129. comfyui_frontend_package/static/assets/Load3D-DHBmC_AU.js +0 -1
  130. comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js +0 -3
  131. comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js.map +0 -1
  132. comfyui_frontend_package/static/assets/WidgetSelect-DsJGH12l.js +0 -1
  133. comfyui_frontend_package/static/assets/api-CUAc7rDA.js.map +0 -1
  134. comfyui_frontend_package/static/assets/auth-D3RiiqZ8.js +0 -1
  135. comfyui_frontend_package/static/assets/firebaseAuthStore-CZgxeMyf.js +0 -1
  136. comfyui_frontend_package/static/assets/releaseStore-CubqSv5t.js +0 -1
  137. comfyui_frontend_package/static/assets/useSubscriptionDialog-BzMzio2H.js +0 -1
  138. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/WHEEL +0 -0
  139. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/utils/envUtil.ts","../../src/platform/distribution/types.ts","../../src/platform/updates/common/toastStore.ts","../../src/platform/workflow/templates/types/template.ts","../../src/platform/workflow/validation/schemas/workflowSchema.ts","../../src/lib/litegraph/src/ContextMenu.ts","../../src/lib/litegraph/src/types/globalEnums.ts","../../src/lib/litegraph/src/measure.ts","../../src/lib/litegraph/src/CurveEditor.ts","../../src/lib/litegraph/src/DragAndScale.ts","../../src/lib/litegraph/src/utils/uuid.ts","../../src/renderer/core/layout/utils/nodeSizeUtil.ts","../../src/renderer/core/layout/types.ts","../../src/renderer/core/layout/constants.ts","../../src/renderer/core/layout/utils/geometry.ts","../../src/renderer/core/layout/utils/layoutMath.ts","../../src/renderer/core/layout/utils/layoutUtils.ts","../../src/renderer/core/layout/utils/mappers.ts","../../src/renderer/core/spatial/QuadTree.ts","../../src/renderer/core/spatial/SpatialIndex.ts","../../src/renderer/core/layout/store/layoutStore.ts","../../src/renderer/core/layout/operations/layoutMutations.ts","../../src/constants/groupNodeConstants.ts","../../src/renderer/core/canvas/pathRenderer.ts","../../src/renderer/core/canvas/litegraph/litegraphLinkAdapter.ts","../../src/lib/litegraph/src/node/slotUtils.ts","../../src/renderer/core/layout/slots/slotIdentifier.ts","../../src/renderer/core/canvas/litegraph/slotCalculations.ts","../../src/types/nodeIdentification.ts","../../src/utils/typeGuardUtil.ts","../../src/utils/graphTraversalUtil.ts","../../src/lib/litegraph/src/CanvasPointer.ts","../../src/lib/litegraph/src/infrastructure/NullGraphError.ts","../../src/lib/litegraph/src/LGraphNodeProperties.ts","../../src/lib/litegraph/src/utils/type.ts","../../src/lib/litegraph/src/LGraphIcon.ts","../../src/lib/litegraph/src/LGraphBadge.ts","../../src/lib/litegraph/src/infrastructure/Rectangle.ts","../../src/lib/litegraph/src/LGraphButton.ts","../../src/lib/litegraph/src/LLink.ts","../../src/lib/litegraph/src/canvas/measureSlots.ts","../../src/lib/litegraph/src/draw.ts","../../src/lib/litegraph/src/node/SlotBase.ts","../../src/lib/litegraph/src/node/NodeSlot.ts","../../src/lib/litegraph/src/Reroute.ts","../../src/lib/litegraph/src/strings.ts","../../src/lib/litegraph/src/utils/collections.ts","../../src/lib/litegraph/src/infrastructure/CustomEventTarget.ts","../../src/lib/litegraph/src/infrastructure/ConstrainedSize.ts","../../src/lib/litegraph/src/subgraph/SubgraphSlotBase.ts","../../src/lib/litegraph/src/subgraph/SubgraphInput.ts","../../src/lib/litegraph/src/subgraph/EmptySubgraphInput.ts","../../src/lib/litegraph/src/subgraph/SubgraphIONodeBase.ts","../../src/lib/litegraph/src/subgraph/SubgraphInputNode.ts","../../src/lib/litegraph/src/subgraph/SubgraphOutput.ts","../../src/lib/litegraph/src/subgraph/EmptySubgraphOutput.ts","../../src/lib/litegraph/src/subgraph/SubgraphOutputNode.ts","../../src/lib/litegraph/src/subgraph/subgraphUtils.ts","../../src/lib/litegraph/src/node/NodeInputSlot.ts","../../src/lib/litegraph/src/node/NodeOutputSlot.ts","../../src/lib/litegraph/src/utils/feedback.ts","../../src/lib/litegraph/src/utils/spaceDistribution.ts","../../src/lib/litegraph/src/utils/textUtils.ts","../../src/i18n.ts","../../src/lib/litegraph/src/widgets/BaseWidget.ts","../../src/lib/litegraph/src/widgets/AssetWidget.ts","../../src/lib/litegraph/src/widgets/BooleanWidget.ts","../../src/lib/litegraph/src/widgets/BoundingBoxWidget.ts","../../src/lib/litegraph/src/widgets/ButtonWidget.ts","../../src/lib/litegraph/src/widgets/ChartWidget.ts","../../src/lib/litegraph/src/widgets/ColorWidget.ts","../../src/lib/litegraph/src/widgets/BaseSteppedWidget.ts","../../src/lib/litegraph/src/widgets/ComboWidget.ts","../../src/lib/litegraph/src/widgets/FileUploadWidget.ts","../../src/lib/litegraph/src/widgets/GalleriaWidget.ts","../../src/lib/litegraph/src/widgets/ImageCompareWidget.ts","../../src/lib/litegraph/src/widgets/ImageCropWidget.ts","../../src/lib/litegraph/src/widgets/KnobWidget.ts","../../src/lib/litegraph/src/widgets/LegacyWidget.ts","../../src/lib/litegraph/src/widgets/MarkdownWidget.ts","../../src/lib/litegraph/src/widgets/MultiSelectWidget.ts","../../src/lib/litegraph/src/widgets/NumberWidget.ts","../../src/lib/litegraph/src/widgets/SelectButtonWidget.ts","../../src/lib/litegraph/src/widgets/SliderWidget.ts","../../src/lib/litegraph/src/widgets/TextWidget.ts","../../src/lib/litegraph/src/widgets/TextareaWidget.ts","../../src/lib/litegraph/src/widgets/TreeSelectWidget.ts","../../src/lib/litegraph/src/widgets/widgetMap.ts","../../src/lib/litegraph/src/LGraphNode.ts","../../src/lib/litegraph/src/LGraphGroup.ts","../../src/lib/litegraph/src/canvas/FloatingRenderLink.ts","../../src/lib/litegraph/src/canvas/MovingLinkBase.ts","../../src/lib/litegraph/src/canvas/MovingInputLink.ts","../../src/lib/litegraph/src/canvas/MovingOutputLink.ts","../../src/lib/litegraph/src/canvas/ToInputFromIoNodeLink.ts","../../src/lib/litegraph/src/canvas/ToInputRenderLink.ts","../../src/lib/litegraph/src/canvas/ToOutputFromIoNodeLink.ts","../../src/lib/litegraph/src/canvas/ToOutputRenderLink.ts","../../src/lib/litegraph/src/canvas/ToOutputFromRerouteLink.ts","../../src/lib/litegraph/src/canvas/LinkConnector.ts","../../src/lib/litegraph/src/infrastructure/RecursionError.ts","../../src/lib/litegraph/src/infrastructure/InvalidLinkError.ts","../../src/lib/litegraph/src/infrastructure/SlotIndexError.ts","../../src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts","../../src/lib/litegraph/src/subgraph/SubgraphNode.ts","../../src/lib/litegraph/src/utils/arrange.ts","../../src/lib/litegraph/src/utils/linkColors.ts","../../src/lib/litegraph/src/LGraphCanvas.ts","../../src/lib/litegraph/src/MapProxyHandler.ts","../../src/lib/litegraph/src/LGraph.ts","../../src/lib/litegraph/src/canvas/InputIndicators.ts","../../src/lib/litegraph/src/LiteGraphGlobal.ts","../../src/lib/litegraph/src/polyfills.ts","../../src/lib/litegraph/src/litegraph.ts","../../src/schemas/colorPaletteSchema.ts","../../src/schemas/keyBindingSchema.ts","../../src/types/nodeSource.ts","../../src/types/searchBoxTypes.ts","../../src/schemas/apiSchema.ts","../../src/platform/remote/comfyui/jobs/jobTypes.ts","../../src/platform/remote/comfyui/jobs/fetchJobs.ts","../../src/scripts/api.ts"],"sourcesContent":["import type { ElectronAPI } from '@comfyorg/comfyui-electron-types'\n\nexport function isElectron() {\n return 'electronAPI' in window && window.electronAPI !== undefined\n}\n\nexport function electronAPI() {\n return (window as any).electronAPI as ElectronAPI\n}\n\nexport function showNativeSystemMenu() {\n electronAPI()?.showContextMenu()\n}\n\nexport function isNativeWindow() {\n return isElectron() && !!window.navigator.windowControlsOverlay?.visible\n}\n","import { isElectron } from '@/utils/envUtil'\n\n/**\n * Distribution types and compile-time constants for managing\n * multi-distribution builds (Desktop, Localhost, Cloud)\n */\n\ntype Distribution = 'desktop' | 'localhost' | 'cloud'\n\ndeclare global {\n const __DISTRIBUTION__: Distribution\n}\n\n/** Current distribution - replaced at compile time */\nconst DISTRIBUTION: Distribution = __DISTRIBUTION__\n\n/** Distribution type checks */\nexport const isDesktop = DISTRIBUTION === 'desktop' || isElectron() // TODO: replace with build var\nexport const isCloud = DISTRIBUTION === 'cloud'\n// export const isLocalhost = DISTRIBUTION === 'localhost' || (!isDesktop && !isCloud)\n","// Within Vue component context, you can directly call useToast().add()\n// instead of going through the store.\n// The store is useful when you need to call it from outside the Vue component context.\nimport { defineStore } from 'pinia'\nimport type { ToastMessageOptions } from 'primevue/toast'\nimport { ref } from 'vue'\n\nexport const useToastStore = defineStore('toast', () => {\n const messagesToAdd = ref<ToastMessageOptions[]>([])\n const messagesToRemove = ref<ToastMessageOptions[]>([])\n const removeAllRequested = ref(false)\n\n function add(message: ToastMessageOptions) {\n messagesToAdd.value = [...messagesToAdd.value, message]\n }\n\n function remove(message: ToastMessageOptions) {\n messagesToRemove.value = [...messagesToRemove.value, message]\n }\n\n function removeAll() {\n removeAllRequested.value = true\n }\n\n function addAlert(message: string) {\n add({ severity: 'warn', summary: 'Alert', detail: message })\n }\n\n return {\n messagesToAdd,\n messagesToRemove,\n removeAllRequested,\n\n add,\n remove,\n removeAll,\n addAlert\n }\n})\n","export interface TemplateInfo {\n name: string\n /**\n * Optional title which is used as the fallback if the name is not in the locales dictionary.\n */\n title?: string\n tutorialUrl?: string\n mediaType: string\n mediaSubtype: string\n thumbnailVariant?: string\n description: string\n localizedTitle?: string\n localizedDescription?: string\n isEssential?: boolean\n sourceModule?: string\n tags?: string[]\n models?: string[]\n date?: string\n useCase?: string\n license?: string\n /**\n * Estimated VRAM requirement in bytes.\n */\n vram?: number\n size?: number\n /**\n * Whether this template uses open source models. When false, indicates partner/API node templates.\n */\n openSource?: boolean\n /**\n * Array of custom node package IDs required for this template (from Custom Node Registry).\n * Templates with this field will be hidden on local installations temporarily.\n */\n requiresCustomNodes?: string[]\n /**\n * Manual ranking boost/demotion for \"Recommended\" sort. Scale 1-10, default 5.\n * Higher values promote the template, lower values demote it.\n */\n searchRank?: number\n /**\n * Usage score based on real world usage statistics.\n * Used for popular templates sort and for \"Recommended\" sort boost.\n */\n usage?: number\n /**\n * Manage template's visibility across different distributions by specifying which distributions it should be included on.\n * If not specified, the template will be included on all distributions.\n */\n includeOnDistributions?: TemplateIncludeOnDistributionEnum[]\n}\n\nexport enum TemplateIncludeOnDistributionEnum {\n Cloud = 'cloud',\n Local = 'local',\n Desktop = 'desktop',\n Mac = 'mac',\n Windows = 'windows'\n}\n\nexport interface WorkflowTemplates {\n moduleName: string\n templates: TemplateInfo[]\n title: string\n localizedTitle?: string\n category?: string\n type?: string\n icon?: string\n isEssential?: boolean\n}\n\nexport interface TemplateGroup {\n label: string\n icon?: string\n modules: WorkflowTemplates[]\n}\n","import { z } from 'zod'\nimport type { SafeParseReturnType } from 'zod'\nimport { fromZodError } from 'zod-validation-error'\nimport type { RendererType } from '@/lib/litegraph/src/LGraph'\n\nconst zRendererType = z.enum(['LG', 'Vue']) satisfies z.ZodType<RendererType>\n\n// GroupNode is hacking node id to be a string, so we need to allow that.\n// innerNode.id = `${this.node.id}:${i}`\n// Remove it after GroupNode is redesigned.\nexport const zNodeId = z.union([z.number().int(), z.string()])\nconst zNodeInputName = z.string()\nexport type NodeId = z.infer<typeof zNodeId>\nconst zSlotIndex = z.union([\n z.number().int(),\n z\n .string()\n .transform((val) => parseInt(val))\n .refine((val) => !isNaN(val), {\n message: 'Invalid number'\n })\n])\n\n// TODO: Investigate usage of array and number as data type usage in custom nodes.\n// Known usage:\n// - https://github.com/rgthree/rgthree-comfy Context Big node is using array as type.\nconst zDataType = z.union([z.string(), z.array(z.string()), z.number()])\n\nconst zVector2 = z.union([\n z\n .object({ 0: z.number(), 1: z.number() })\n .passthrough()\n .transform((v) => [v[0], v[1]] as [number, number]),\n z.tuple([z.number(), z.number()])\n])\n\n// Definition of an AI model file used in the workflow.\nconst zModelFile = z.object({\n name: z.string(),\n url: z.string().url(),\n hash: z.string().optional(),\n hash_type: z.string().optional(),\n directory: z.string()\n})\n\nconst zGraphState = z\n .object({\n lastGroupId: z.number(),\n lastNodeId: z.number(),\n lastLinkId: z.number(),\n lastRerouteId: z.number()\n })\n .passthrough()\n\nconst zComfyLink = z.tuple([\n z.number(), // Link id\n zNodeId, // Node id of source node\n zSlotIndex, // Output slot# of source node\n zNodeId, // Node id of destination node\n zSlotIndex, // Input slot# of destination node\n zDataType // Data type\n])\n\n/** Extension to 0.4 schema (links as arrays): parent reroute ID */\nconst zComfyLinkExtension = z\n .object({\n id: z.number(),\n parentId: z.number()\n })\n .passthrough()\n\nconst zComfyLinkObject = z\n .object({\n id: z.number(),\n origin_id: zNodeId,\n origin_slot: zSlotIndex,\n target_id: zNodeId,\n target_slot: zSlotIndex,\n type: zDataType,\n parentId: z.number().optional()\n })\n .passthrough()\n\nconst zReroute = z\n .object({\n id: z.number(),\n parentId: z.number().optional(),\n pos: zVector2,\n linkIds: z.array(z.number()).nullish(),\n floating: z\n .object({\n slotType: z.enum(['input', 'output'])\n })\n .optional()\n })\n .passthrough()\n\nconst zNodeOutput = z\n .object({\n name: z.string(),\n type: zDataType,\n links: z.array(z.number()).nullable().optional(),\n slot_index: zSlotIndex.optional()\n })\n .passthrough()\n\nconst zNodeInput = z\n .object({\n name: zNodeInputName,\n type: zDataType,\n link: z.number().nullable().optional(),\n slot_index: zSlotIndex.optional()\n })\n .passthrough()\n\nconst zFlags = z\n .object({\n collapsed: z.boolean().optional(),\n pinned: z.boolean().optional(),\n allow_interaction: z.boolean().optional(),\n horizontal: z.boolean().optional(),\n skip_repeated_outputs: z.boolean().optional()\n })\n .passthrough()\n\nconst repoLikeIdPattern = /^[a-zA-Z0-9](?:[a-zA-Z0-9._-]*[a-zA-Z0-9])?$/\nconst githubUsernamePattern = /^(?!-)(?!.*--)[a-zA-Z0-9-]+(?<!-)$/\nconst gitHashPattern = /^[0-9a-f]{4,40}$/i\nconst semverPattern =\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([\\da-z-]+(?:\\.[\\da-z-]+)*))?(?:\\+([\\da-z-]+(?:\\.[\\da-z-]+)*))?$/\n\n// Shared schema for Comfy Node Registry IDs and GitHub repo names\nconst zRepoLikeId = z\n .string()\n .min(1)\n .max(100)\n .regex(repoLikeIdPattern, {\n message: \"ID can only contain ASCII letters, digits, '_', '-', and '.'\"\n })\n .refine((id) => !/^[_\\-.]|[_\\-.]$/.test(id), {\n message: \"ID must not start or end with '_', '-', or '.'\"\n })\n\nconst zCnrId = zRepoLikeId\nconst zGithubRepoName = zRepoLikeId\n\n// GitHub username/organization schema\nconst zGithubUsername = z\n .string()\n .min(1)\n .max(39)\n .regex(githubUsernamePattern, 'Invalid GitHub username/org')\n\n// Auxiliary ID identifies node packs not installed via the Comfy Node Registry\nconst zAuxId = z\n .string()\n .regex(/^[^/]+\\/[^/]+$/, \"Invalid format. Must be 'github-user/repo-name'\")\n .transform((id) => id.split('/'))\n .refine(\n ([username, repo]) =>\n zGithubUsername.safeParse(username).success &&\n zGithubRepoName.safeParse(repo).success,\n \"Invalid aux_id: Must be valid 'github-username/github-repo-name'\"\n )\n .transform(([username, repo]) => `${username}/${repo}`)\n\nconst zGitHash = z.string().superRefine((val: string, ctx) => {\n if (!gitHashPattern.test(val)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Node pack version has invalid Git commit hash: \"${val}\"`\n })\n }\n})\nconst zSemVer = z.string().superRefine((val: string, ctx) => {\n if (!semverPattern.test(val)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Node pack version has invalid semantic version: \"${val}\"`\n })\n }\n})\nconst zVersion = z.union([\n z\n .string()\n .transform((ver) => ver.replace(/^v/, '')) // Strip leading 'v'\n .pipe(z.union([zSemVer, zGitHash])),\n z.literal('unknown')\n])\n\nconst zProperties = z\n .object({\n ['Node name for S&R']: z.string().optional(),\n cnr_id: zCnrId.optional(),\n aux_id: zAuxId.optional(),\n ver: zVersion.optional(),\n models: z.array(zModelFile).optional()\n })\n .passthrough()\n\nconst zWidgetValues = z.union([z.array(z.any()), z.record(z.any())])\n\nconst zComfyNode = z\n .object({\n id: zNodeId,\n type: z.string(),\n pos: zVector2,\n size: zVector2,\n flags: zFlags,\n order: z.number(),\n mode: z.number(),\n inputs: z.array(zNodeInput).optional(),\n outputs: z.array(zNodeOutput).optional(),\n properties: zProperties,\n widgets_values: zWidgetValues.optional(),\n color: z.string().optional(),\n bgcolor: z.string().optional()\n })\n .passthrough()\n\nconst zSubgraphIO = zNodeInput.extend({\n /** Slot ID (internal; never changes once instantiated). */\n id: z.string().uuid(),\n /** The data type this slot uses. Unlike nodes, this does not support legacy numeric types. */\n type: z.string(),\n /** Links connected to this slot, or `undefined` if not connected. An output slot should only ever have one link. */\n linkIds: z.array(z.number()).optional()\n})\n\nconst zSubgraphInstance = z\n .object({\n id: zNodeId,\n type: z.string().uuid(),\n pos: zVector2,\n size: zVector2,\n flags: zFlags,\n order: z.number(),\n mode: z.number(),\n inputs: z.array(zSubgraphIO).optional(),\n outputs: z.array(zSubgraphIO).optional(),\n widgets_values: zWidgetValues.optional(),\n color: z.string().optional(),\n bgcolor: z.string().optional()\n })\n .passthrough()\n\nconst zGroup = z\n .object({\n id: z.number().optional(),\n title: z.string(),\n bounding: z.tuple([z.number(), z.number(), z.number(), z.number()]),\n color: z.string().optional(),\n font_size: z.number().optional(),\n locked: z.boolean().optional()\n })\n .passthrough()\n\nconst zDS = z\n .object({\n scale: z.number(),\n offset: zVector2\n })\n .passthrough()\n\nconst zConfig = z\n .object({\n links_ontop: z.boolean().optional(),\n align_to_grid: z.boolean().optional()\n })\n .passthrough()\n\nconst zExtra = z\n .object({\n ds: zDS.optional(),\n frontendVersion: z.string().optional(),\n linkExtensions: z.array(zComfyLinkExtension).optional(),\n reroutes: z.array(zReroute).optional(),\n workflowRendererVersion: zRendererType.optional()\n })\n .passthrough()\n\nconst zGraphDefinitions = z.object({\n subgraphs: z.lazy(() => z.array(zSubgraphDefinition))\n})\n\nconst zBaseExportableGraph = z.object({\n /** Unique graph ID. Automatically generated if not provided. */\n id: z.string().uuid().optional(),\n revision: z.number().optional(),\n config: zConfig.optional().nullable(),\n /** Details of the appearance and location of subgraphs shown in this graph. Similar to */\n subgraphs: z.array(zSubgraphInstance).optional()\n})\n\n/** Schema version 0.4 */\nexport const zComfyWorkflow = zBaseExportableGraph\n .extend({\n id: z.string().uuid().optional(),\n revision: z.number().optional(),\n last_node_id: zNodeId,\n last_link_id: z.number(),\n nodes: z.array(zComfyNode),\n links: z.array(zComfyLink),\n floatingLinks: z.array(zComfyLinkObject).optional(),\n groups: z.array(zGroup).optional(),\n config: zConfig.optional().nullable(),\n extra: zExtra.optional().nullable(),\n version: z.number(),\n models: z.array(zModelFile).optional(),\n definitions: zGraphDefinitions.optional()\n })\n .passthrough()\n\n/** Required for recursive definition of subgraphs. */\ninterface ComfyWorkflow1BaseType {\n id?: string\n revision?: number\n version: 1\n models?: z.infer<typeof zModelFile>[]\n state: z.infer<typeof zGraphState>\n}\n\n/** Required for recursive definition of subgraphs w/ZodEffects. */\ninterface ComfyWorkflow1BaseInput extends ComfyWorkflow1BaseType {\n groups?: z.input<typeof zGroup>[]\n nodes: z.input<typeof zComfyNode>[]\n links?: z.input<typeof zComfyLinkObject>[]\n floatingLinks?: z.input<typeof zComfyLinkObject>[]\n reroutes?: z.input<typeof zReroute>[]\n definitions?: {\n subgraphs: SubgraphDefinitionBase<ComfyWorkflow1BaseInput>[]\n }\n}\n\n/** Required for recursive definition of subgraphs w/ZodEffects. */\ninterface ComfyWorkflow1BaseOutput extends ComfyWorkflow1BaseType {\n groups?: z.output<typeof zGroup>[]\n nodes: z.output<typeof zComfyNode>[]\n links?: z.output<typeof zComfyLinkObject>[]\n floatingLinks?: z.output<typeof zComfyLinkObject>[]\n reroutes?: z.output<typeof zReroute>[]\n definitions?: {\n subgraphs: SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>[]\n }\n}\n\n/** Schema version 1 */\nexport const zComfyWorkflow1 = zBaseExportableGraph\n .extend({\n id: z.string().uuid().optional(),\n revision: z.number().optional(),\n version: z.literal(1),\n config: zConfig.optional().nullable(),\n state: zGraphState,\n groups: z.array(zGroup).optional(),\n nodes: z.array(zComfyNode),\n links: z.array(zComfyLinkObject).optional(),\n floatingLinks: z.array(zComfyLinkObject).optional(),\n reroutes: z.array(zReroute).optional(),\n extra: zExtra.optional().nullable(),\n models: z.array(zModelFile).optional(),\n definitions: z\n .object({\n subgraphs: z.lazy(\n (): z.ZodArray<\n z.ZodType<\n SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>,\n z.ZodTypeDef,\n SubgraphDefinitionBase<ComfyWorkflow1BaseInput>\n >,\n 'many'\n > => z.array(zSubgraphDefinition)\n )\n })\n .optional()\n })\n .passthrough()\n\nconst zExportedSubgraphIONode = z.object({\n id: zNodeId,\n bounding: z.tuple([z.number(), z.number(), z.number(), z.number()]),\n pinned: z.boolean().optional()\n})\n\nconst zExposedWidget = z.object({\n id: z.string(),\n name: z.string()\n})\n\ninterface SubgraphDefinitionBase<\n T extends ComfyWorkflow1BaseInput | ComfyWorkflow1BaseOutput\n> {\n /** Unique graph ID. Automatically generated if not provided. */\n id: string\n revision: number\n name: string\n\n inputNode: T extends ComfyWorkflow1BaseInput\n ? z.input<typeof zExportedSubgraphIONode>\n : z.output<typeof zExportedSubgraphIONode>\n outputNode: T extends ComfyWorkflow1BaseInput\n ? z.input<typeof zExportedSubgraphIONode>\n : z.output<typeof zExportedSubgraphIONode>\n /** Ordered list of inputs to the subgraph itself. Similar to a reroute, with the input side in the graph, and the output side in the subgraph. */\n inputs?: T extends ComfyWorkflow1BaseInput\n ? z.input<typeof zSubgraphIO>[]\n : z.output<typeof zSubgraphIO>[]\n /** Ordered list of outputs from the subgraph itself. Similar to a reroute, with the input side in the subgraph, and the output side in the graph. */\n outputs?: T extends ComfyWorkflow1BaseInput\n ? z.input<typeof zSubgraphIO>[]\n : z.output<typeof zSubgraphIO>[]\n /** A list of node widgets displayed in the parent graph, on the subgraph object. */\n widgets?: T extends ComfyWorkflow1BaseInput\n ? z.input<typeof zExposedWidget>[]\n : z.output<typeof zExposedWidget>[]\n definitions?: {\n subgraphs: SubgraphDefinitionBase<T>[]\n }\n}\n\n/** A subgraph definition `worfklow.definitions.subgraphs` */\nconst zSubgraphDefinition = zComfyWorkflow1\n .extend({\n /** Unique graph ID. Automatically generated if not provided. */\n id: z.string().uuid(),\n revision: z.number(),\n name: z.string(),\n inputNode: zExportedSubgraphIONode,\n outputNode: zExportedSubgraphIONode,\n\n /** Ordered list of inputs to the subgraph itself. Similar to a reroute, with the input side in the graph, and the output side in the subgraph. */\n inputs: z.array(zSubgraphIO).optional(),\n /** Ordered list of outputs from the subgraph itself. Similar to a reroute, with the input side in the subgraph, and the output side in the graph. */\n outputs: z.array(zSubgraphIO).optional(),\n /** A list of node widgets displayed in the parent graph, on the subgraph object. */\n widgets: z.array(zExposedWidget).optional(),\n definitions: z\n .object({\n subgraphs: z.lazy(\n (): z.ZodArray<\n z.ZodType<\n SubgraphDefinitionBase<ComfyWorkflow1BaseInput>,\n z.ZodTypeDef,\n SubgraphDefinitionBase<ComfyWorkflow1BaseInput>\n >,\n 'many'\n > => zSubgraphDefinition.array()\n )\n })\n .optional()\n })\n .passthrough()\n\nexport type ModelFile = z.infer<typeof zModelFile>\nexport type ComfyLinkObject = z.infer<typeof zComfyLinkObject>\nexport type ComfyNode = z.infer<typeof zComfyNode>\nexport type Reroute = z.infer<typeof zReroute>\nexport type WorkflowJSON04 = z.infer<typeof zComfyWorkflow>\nexport type ComfyWorkflowJSON = z.infer<\n typeof zComfyWorkflow | typeof zComfyWorkflow1\n>\ntype SubgraphDefinition = z.infer<typeof zSubgraphDefinition>\n\n/**\n * Type guard to check if an object is a SubgraphDefinition.\n * This helps TypeScript understand the type when z.lazy() breaks inference.\n */\nexport function isSubgraphDefinition(obj: unknown): obj is SubgraphDefinition {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n 'id' in obj &&\n 'name' in obj &&\n 'nodes' in obj &&\n Array.isArray((obj as SubgraphDefinition).nodes) &&\n 'inputNode' in obj &&\n 'outputNode' in obj\n )\n}\n\nconst zWorkflowVersion = z.object({\n version: z.number()\n})\n\nexport async function validateComfyWorkflow(\n data: unknown,\n onError: (error: string) => void = console.warn\n): Promise<ComfyWorkflowJSON | null> {\n const versionResult = zWorkflowVersion.safeParse(data)\n\n let result: SafeParseReturnType<unknown, ComfyWorkflowJSON>\n if (!versionResult.success) {\n // Invalid workflow\n const error = fromZodError(versionResult.error)\n onError(`Workflow does not contain a valid version. Zod error:\\n${error}`)\n return null\n } else if (versionResult.data.version === 1) {\n // Schema version 1\n result = await zComfyWorkflow1.safeParseAsync(data)\n } else {\n // Unknown or old version: 0.4\n result = await zComfyWorkflow.safeParseAsync(data)\n }\n if (result.success) return result.data\n\n const error = fromZodError(result.error)\n onError(`Invalid workflow against zod schema:\\n${error}`)\n return null\n}\n\n/**\n * API format workflow for direct API usage.\n */\nconst zNodeInputValue = z.union([\n // For widget values (can be any type)\n\n z.any(),\n // For node links [nodeId, slotIndex]\n z.tuple([zNodeId, zSlotIndex])\n])\n\nconst zNodeData = z.object({\n inputs: z.record(zNodeInputName, zNodeInputValue),\n class_type: z.string(),\n _meta: z.object({\n title: z.string()\n })\n})\n\nconst zComfyApiWorkflow = z.record(zNodeId, zNodeData)\nexport type ComfyApiWorkflow = z.infer<typeof zComfyApiWorkflow>\n","import type {\n ContextMenuDivElement,\n IContextMenuOptions,\n IContextMenuValue\n} from './interfaces'\nimport { LiteGraph } from './litegraph'\n\n// TODO: Replace this pattern with something more modern.\nexport interface ContextMenu<TValue = unknown> {\n constructor: new (\n ...args: ConstructorParameters<typeof ContextMenu<TValue>>\n ) => ContextMenu<TValue>\n}\n\n/**\n * ContextMenu from LiteGUI\n */\n\nexport class ContextMenu<TValue = unknown> {\n options: IContextMenuOptions<TValue>\n parentMenu?: ContextMenu<TValue>\n root: ContextMenuDivElement<TValue>\n current_submenu?: ContextMenu<TValue>\n lock?: boolean\n\n controller: AbortController = new AbortController()\n\n /**\n * @todo Interface for values requires functionality change - currently accepts\n * an array of strings, functions, objects, nulls, or undefined.\n * @param values (allows object { title: \"Nice text\", callback: function ... })\n * @param options [optional] Some options:\\\n * - title: title to show on top of the menu\n * - callback: function to call when an option is clicked, it receives the item information\n * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback\n * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position\n */\n constructor(\n values: readonly (string | IContextMenuValue<TValue> | null)[],\n options: IContextMenuOptions<TValue>\n ) {\n options ||= {}\n this.options = options\n\n // to link a menu with its parent\n const parent = options.parentMenu\n if (parent) {\n if (!(parent instanceof ContextMenu)) {\n console.error('parentMenu must be of class ContextMenu, ignoring it')\n options.parentMenu = undefined\n } else {\n this.parentMenu = parent\n this.parentMenu.lock = true\n this.parentMenu.current_submenu = this\n }\n if (parent.options?.className === 'dark') {\n options.className = 'dark'\n }\n }\n\n // use strings because comparing classes between windows doesnt work\n const eventClass = options.event ? options.event.constructor.name : null\n if (\n eventClass !== 'MouseEvent' &&\n eventClass !== 'CustomEvent' &&\n eventClass !== 'PointerEvent'\n ) {\n console.error(\n `Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. (${eventClass})`\n )\n options.event = undefined\n }\n\n const root: ContextMenuDivElement<TValue> = document.createElement('div')\n let classes = 'litegraph litecontextmenu litemenubar-panel'\n if (options.className) classes += ` ${options.className}`\n root.className = classes\n root.style.minWidth = '100'\n root.style.minHeight = '100'\n\n // Close the context menu when a click occurs outside this context menu or its submenus\n const { signal } = this.controller\n const eventOptions = { capture: true, signal }\n\n if (!this.parentMenu) {\n document.addEventListener(\n 'pointerdown',\n (e) => {\n if (e.target instanceof Node && !this.containsNode(e.target)) {\n this.close()\n }\n },\n eventOptions\n )\n }\n\n // this prevents the default context browser menu to open in case this menu was created when pressing right button\n root.addEventListener('pointerup', (e) => e.preventDefault(), eventOptions)\n\n // Right button\n root.addEventListener(\n 'contextmenu',\n (e) => {\n if (e.button === 2) e.preventDefault()\n },\n eventOptions\n )\n\n root.addEventListener(\n 'pointerdown',\n (e) => {\n if (e.button == 2) {\n this.close()\n e.preventDefault()\n }\n },\n eventOptions\n )\n\n this.root = root\n\n // title\n if (options.title) {\n const element = document.createElement('div')\n element.className = 'litemenu-title'\n element.innerHTML = options.title\n root.append(element)\n }\n\n // entries\n for (let i = 0; i < values.length; i++) {\n const value = values[i]\n let name = Array.isArray(values) ? value : String(i)\n\n if (typeof name !== 'string') {\n name =\n name != null\n ? name.content === undefined\n ? String(name)\n : name.content\n : name\n }\n\n this.addItem(name, value, options)\n }\n\n // insert before checking position\n const ownerDocument = (options.event?.target as Node | null | undefined)\n ?.ownerDocument\n const root_document = ownerDocument || document\n\n if (root_document.fullscreenElement)\n root_document.fullscreenElement.append(root)\n else root_document.body.append(root)\n\n // compute best position\n let left = options.left || 0\n let top = options.top || 0\n if (options.event) {\n left = options.event.clientX - 10\n top = options.event.clientY - 10\n if (options.title) top -= 20\n\n if (parent) {\n const rect = parent.root.getBoundingClientRect()\n left = rect.left + rect.width\n }\n\n const body_rect = document.body.getBoundingClientRect()\n const root_rect = root.getBoundingClientRect()\n if (body_rect.height == 0)\n console.error(\n 'document.body height is 0. That is dangerous, set html,body { height: 100%; }'\n )\n\n if (body_rect.width && left > body_rect.width - root_rect.width - 10)\n left = body_rect.width - root_rect.width - 10\n if (body_rect.height && top > body_rect.height - root_rect.height - 10)\n top = body_rect.height - root_rect.height - 10\n }\n\n root.style.left = `${left}px`\n root.style.top = `${top}px`\n\n if (LiteGraph.context_menu_scaling && options.scale) {\n root.style.transform = `scale(${Math.round(options.scale * 4) * 0.25})`\n }\n }\n\n /**\n * Checks if {@link node} is inside this context menu or any of its submenus\n * @param node The {@link Node} to check\n * @param visited A set of visited menus to avoid circular references\n * @returns `true` if {@link node} is inside this context menu or any of its submenus\n */\n containsNode(node: Node, visited: Set<this> = new Set()): boolean {\n if (visited.has(this)) return false\n visited.add(this)\n\n return (\n this.current_submenu?.containsNode(node, visited) ||\n this.root.contains(node)\n )\n }\n\n addItem(\n name: string | null,\n value: string | IContextMenuValue<TValue> | null,\n options: IContextMenuOptions<TValue>\n ): HTMLElement {\n options ||= {}\n\n const element: ContextMenuDivElement<TValue> = document.createElement('div')\n element.className = 'litemenu-entry submenu'\n\n let disabled = false\n\n if (value === null) {\n element.classList.add('separator')\n } else {\n const innerHtml = name === null ? '' : String(name)\n if (typeof value === 'string') {\n element.innerHTML = innerHtml\n } else {\n element.innerHTML = value?.title ?? innerHtml\n\n if (value.disabled) {\n disabled = true\n element.classList.add('disabled')\n element.setAttribute('aria-disabled', 'true')\n }\n if (value.submenu || value.has_submenu) {\n element.classList.add('has_submenu')\n element.setAttribute('aria-haspopup', 'true')\n element.setAttribute('aria-expanded', 'false')\n }\n if (value.className) element.className += ` ${value.className}`\n }\n element.value = value\n element.setAttribute('role', 'menuitem')\n\n if (typeof value === 'function') {\n element.dataset['value'] = String(name)\n element.onclick_callback = value\n } else {\n element.dataset['value'] = String(value)\n }\n }\n\n this.root.append(element)\n if (!disabled) element.addEventListener('click', inner_onclick)\n if (!disabled && options.autoopen)\n element.addEventListener('pointerenter', inner_over)\n\n const setAriaExpanded = () => {\n const entries = this.root.querySelectorAll(\n 'div.litemenu-entry.has_submenu'\n )\n if (entries) {\n for (const entry of entries) {\n entry.setAttribute('aria-expanded', 'false')\n }\n }\n element.setAttribute('aria-expanded', 'true')\n }\n\n function inner_over(this: ContextMenuDivElement<TValue>, e: MouseEvent) {\n const value = this.value\n if (!value || !(value as IContextMenuValue).has_submenu) return\n\n // if it is a submenu, autoopen like the item was clicked\n inner_onclick.call(this, e)\n setAriaExpanded()\n }\n\n // menu option clicked\n\n const that = this\n function inner_onclick(this: ContextMenuDivElement<TValue>, e: MouseEvent) {\n const value = this.value\n let close_parent = true\n\n that.current_submenu?.close(e)\n if (\n (value as IContextMenuValue)?.has_submenu ||\n (value as IContextMenuValue)?.submenu\n ) {\n setAriaExpanded()\n }\n\n // global callback\n if (options.callback) {\n const r = options.callback.call(\n this,\n value,\n options,\n e,\n that,\n options.node\n )\n if (r === true) close_parent = false\n }\n\n // special cases\n if (typeof value === 'object') {\n if (\n value.callback &&\n !options.ignore_item_callbacks &&\n value.disabled !== true\n ) {\n // item callback\n const r = value.callback.call(\n this,\n value,\n options,\n e,\n that,\n options.extra\n )\n if (r === true) close_parent = false\n }\n if (value.submenu) {\n if (!value.submenu.options) throw 'ContextMenu submenu needs options'\n\n new that.constructor(value.submenu.options, {\n callback: value.submenu.callback,\n event: e,\n parentMenu: that,\n ignore_item_callbacks: value.submenu.ignore_item_callbacks,\n title: value.submenu.title,\n extra: value.submenu.extra,\n autoopen: options.autoopen\n })\n close_parent = false\n }\n }\n\n if (close_parent && !that.lock) that.close()\n }\n\n return element\n }\n\n close(e?: MouseEvent, ignore_parent_menu?: boolean): void {\n this.controller.abort()\n this.root.remove()\n if (this.parentMenu && !ignore_parent_menu) {\n this.parentMenu.lock = false\n this.parentMenu.current_submenu = undefined\n if (e === undefined) {\n this.parentMenu.close()\n } else if (\n e &&\n !ContextMenu.isCursorOverElement(e, this.parentMenu.root)\n ) {\n ContextMenu.trigger(\n this.parentMenu.root,\n `${LiteGraph.pointerevents_method}leave`,\n e\n )\n }\n }\n this.current_submenu?.close(e, true)\n }\n\n /** @deprecated Likely unused, however code search was inconclusive (too many results to check by hand). */\n // this code is used to trigger events easily (used in the context menu mouseleave\n static trigger(\n element: HTMLDivElement,\n event_name: string,\n params: MouseEvent\n ): CustomEvent {\n const evt = document.createEvent('CustomEvent')\n evt.initCustomEvent(event_name, true, true, params)\n if (element.dispatchEvent) element.dispatchEvent(evt)\n // else nothing seems bound here so nothing to do\n return evt\n }\n\n // returns the top most menu\n getTopMenu(): ContextMenu<TValue> {\n return this.options.parentMenu ? this.options.parentMenu.getTopMenu() : this\n }\n\n getFirstEvent(): MouseEvent | undefined {\n return this.options.parentMenu\n ? this.options.parentMenu.getFirstEvent()\n : this.options.event\n }\n\n /** @deprecated Unused. */\n static isCursorOverElement(\n event: MouseEvent,\n element: HTMLDivElement\n ): boolean {\n const left = event.clientX\n const top = event.clientY\n const rect = element.getBoundingClientRect()\n if (!rect) return false\n\n if (\n top > rect.top &&\n top < rect.top + rect.height &&\n left > rect.left &&\n left < rect.left + rect.width\n ) {\n return true\n }\n return false\n }\n}\n","/** Node slot type - input or output */\nexport enum NodeSlotType {\n INPUT = 1,\n OUTPUT = 2\n}\n\n/** Shape that an object will render as - used by nodes and slots */\nexport enum RenderShape {\n /** Rectangle with square corners */\n BOX = 1,\n /** Rounded rectangle */\n ROUND = 2,\n /** Circle is circle */\n CIRCLE = 3,\n /** Two rounded corners: top left & bottom right */\n CARD = 4,\n /** Slot shape: Arrow */\n ARROW = 5,\n /** Slot shape: Grid */\n GRID = 6,\n /** Slot shape: Hollow circle */\n HollowCircle = 7\n}\n\n/** Bit flags used to indicate what the pointer is currently hovering over. */\nexport enum CanvasItem {\n /** No items / none */\n Nothing = 0,\n /** At least one node */\n Node = 1,\n /** At least one group */\n Group = 1 << 1,\n /** A reroute (not its path) */\n Reroute = 1 << 2,\n /** The path of a link */\n Link = 1 << 3,\n /** A reroute slot */\n RerouteSlot = 1 << 5,\n /** A subgraph input or output node */\n SubgraphIoNode = 1 << 6,\n /** A subgraph input or output slot */\n SubgraphIoSlot = 1 << 7\n}\n\n/** The direction that a link point will flow towards - e.g. horizontal outputs are right by default */\nexport enum LinkDirection {\n NONE = 0,\n UP = 1,\n DOWN = 2,\n LEFT = 3,\n RIGHT = 4,\n CENTER = 5\n}\n\n/** The path calculation that links follow */\nexport enum LinkRenderType {\n HIDDEN_LINK = -1,\n /** Juts out from the input & output a little @see LinkDirection, then a straight line between them */\n STRAIGHT_LINK = 0,\n /** 90° angles, clean and box-like */\n LINEAR_LINK = 1,\n /** Smooth curved links - default */\n SPLINE_LINK = 2\n}\n\n/** The marker in the middle of a link */\nexport enum LinkMarkerShape {\n /** Do not display markers */\n None = 0,\n /** Circles (default) */\n Circle = 1,\n /** Directional arrows */\n Arrow = 2\n}\n\nexport enum TitleMode {\n NORMAL_TITLE = 0,\n NO_TITLE = 1,\n TRANSPARENT_TITLE = 2,\n AUTOHIDE_TITLE = 3\n}\n\nexport enum LGraphEventMode {\n ALWAYS = 0,\n ON_EVENT = 1,\n NEVER = 2,\n ON_TRIGGER = 3,\n BYPASS = 4\n}\n\nexport enum EaseFunction {\n EASE_IN_OUT_QUAD = 'easeInOutQuad'\n}\n\n/** Bit flags used to indicate what the pointer is currently hovering over. */\nexport enum Alignment {\n /** No items / none */\n None = 0,\n /** Top */\n Top = 1,\n /** Bottom */\n Bottom = 1 << 1,\n /** Vertical middle */\n Middle = 1 << 2,\n /** Left */\n Left = 1 << 3,\n /** Right */\n Right = 1 << 4,\n /** Horizontal centre */\n Centre = 1 << 5,\n /** Top left */\n TopLeft = Top | Left,\n /** Top side, horizontally centred */\n TopCentre = Top | Centre,\n /** Top right */\n TopRight = Top | Right,\n /** Left side, vertically centred */\n MidLeft = Left | Middle,\n /** Middle centre */\n MidCentre = Middle | Centre,\n /** Right side, vertically centred */\n MidRight = Right | Middle,\n /** Bottom left */\n BottomLeft = Bottom | Left,\n /** Bottom side, horizontally centred */\n BottomCentre = Bottom | Centre,\n /** Bottom right */\n BottomRight = Bottom | Right\n}\n\n/**\n * Checks if the bitwise {@link flag} is set in the {@link flagSet}.\n * @param flagSet The unknown set of flags - will be checked for the presence of {@link flag}\n * @param flag The flag to check for\n * @returns `true` if the flag is set, `false` otherwise.\n */\nexport function hasFlag(flagSet: number, flag: number): boolean {\n return (flagSet & flag) === flag\n}\n","import type { HasBoundingRect, Point, ReadOnlyRect, Rect } from './interfaces'\nimport { Alignment, LinkDirection, hasFlag } from './types/globalEnums'\n\n/**\n * Calculates the distance between two points (2D vector)\n * @param a Point a as `x, y`\n * @param b Point b as `x, y`\n * @returns Distance between point {@link a} & {@link b}\n */\nexport function distance(a: Readonly<Point>, b: Readonly<Point>): number {\n return Math.sqrt(\n (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])\n )\n}\n\n/**\n * Calculates the distance2 (squared) between two points (2D vector).\n * Much faster when only comparing distances (closest/furthest point).\n * @param x1 Origin point X\n * @param y1 Origin point Y\n * @param x2 Destination point X\n * @param y2 Destination point Y\n * @returns Distance2 (squared) between point [{@link x1}, {@link y1}] & [{@link x2}, {@link y2}]\n */\nexport function dist2(x1: number, y1: number, x2: number, y2: number): number {\n return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)\n}\n\n/**\n * Determines whether a point is inside a rectangle.\n *\n * Otherwise identical to {@link isInsideRectangle}, it also returns `true` if `x` equals `left` or `y` equals `top`.\n * @param x Point x\n * @param y Point y\n * @param left Rect x\n * @param top Rect y\n * @param width Rect width\n * @param height Rect height\n * @returns `true` if the point is inside the rect, otherwise `false`\n */\nexport function isInRectangle(\n x: number,\n y: number,\n left: number,\n top: number,\n width: number,\n height: number\n): boolean {\n return x >= left && x < left + width && y >= top && y < top + height\n}\n\n/**\n * Determines whether a {@link Point} is inside a {@link Rect}.\n * @param point The point to check, as `x, y`\n * @param rect The rectangle, as `x, y, width, height`\n * @returns `true` if the point is inside the rect, otherwise `false`\n */\nexport function isPointInRect(\n point: Readonly<Point>,\n rect: ReadOnlyRect\n): boolean {\n return (\n point[0] >= rect[0] &&\n point[0] < rect[0] + rect[2] &&\n point[1] >= rect[1] &&\n point[1] < rect[1] + rect[3]\n )\n}\n\n/**\n * Determines whether the point represented by {@link x}, {@link y} is inside a {@link Rect}.\n * @param x X co-ordinate of the point to check\n * @param y Y co-ordinate of the point to check\n * @param rect The rectangle, as `x, y, width, height`\n * @returns `true` if the point is inside the rect, otherwise `false`\n */\nexport function isInRect(x: number, y: number, rect: ReadOnlyRect): boolean {\n return (\n x >= rect[0] &&\n x < rect[0] + rect[2] &&\n y >= rect[1] &&\n y < rect[1] + rect[3]\n )\n}\n\n/**\n * Determines whether a point (`x, y`) is inside a rectangle.\n *\n * This is the original litegraph implementation. It returns `false` if `x` is equal to `left`, or `y` is equal to `top`.\n * @deprecated\n * Use {@link isInRectangle} to match inclusive of top left.\n * This function returns a false negative when an integer point (e.g. pixel) is on the leftmost or uppermost edge of a rectangle.\n * @param x Point x\n * @param y Point y\n * @param left Rect x\n * @param top Rect y\n * @param width Rect width\n * @param height Rect height\n * @returns `true` if the point is inside the rect, otherwise `false`\n */\nexport function isInsideRectangle(\n x: number,\n y: number,\n left: number,\n top: number,\n width: number,\n height: number\n): boolean {\n return left < x && left + width > x && top < y && top + height > y\n}\n\n/**\n * Determines if two rectangles have any overlap\n * @param a Rectangle A as `x, y, width, height`\n * @param b Rectangle B as `x, y, width, height`\n * @returns `true` if rectangles overlap, otherwise `false`\n */\nexport function overlapBounding(a: ReadOnlyRect, b: ReadOnlyRect): boolean {\n const aRight = a[0] + a[2]\n const aBottom = a[1] + a[3]\n const bRight = b[0] + b[2]\n const bBottom = b[1] + b[3]\n\n return a[0] > bRight || a[1] > bBottom || aRight < b[0] || aBottom < b[1]\n ? false\n : true\n}\n\n/**\n * Returns the centre of a rectangle.\n * @param rect The rectangle, as `x, y, width, height`\n * @returns The centre of the rectangle, as `x, y`\n */\nexport function getCentre(rect: ReadOnlyRect): Point {\n return [rect[0] + rect[2] * 0.5, rect[1] + rect[3] * 0.5]\n}\n\n/**\n * Determines if rectangle {@link a} contains the centre point of rectangle {@link b}.\n * @param a Container rectangle A as `x, y, width, height`\n * @param b Sub-rectangle B as `x, y, width, height`\n * @returns `true` if {@link a} contains most of {@link b}, otherwise `false`\n */\nexport function containsCentre(a: ReadOnlyRect, b: ReadOnlyRect): boolean {\n const centreX = b[0] + b[2] * 0.5\n const centreY = b[1] + b[3] * 0.5\n return isInRect(centreX, centreY, a)\n}\n\n/**\n * Determines if rectangle {@link a} wholly contains rectangle {@link b}.\n * @param a Container rectangle A as `x, y, width, height`\n * @param b Sub-rectangle B as `x, y, width, height`\n * @returns `true` if {@link a} wholly contains {@link b}, otherwise `false`\n */\nexport function containsRect(a: ReadOnlyRect, b: ReadOnlyRect): boolean {\n const aRight = a[0] + a[2]\n const aBottom = a[1] + a[3]\n const bRight = b[0] + b[2]\n const bBottom = b[1] + b[3]\n\n const identical =\n a[0] === b[0] && a[1] === b[1] && aRight === bRight && aBottom === bBottom\n\n return (\n !identical &&\n a[0] <= b[0] &&\n a[1] <= b[1] &&\n aRight >= bRight &&\n aBottom >= bBottom\n )\n}\n\n/**\n * Adds an offset in the specified direction to {@link out}\n * @param amount Amount of offset to add\n * @param direction Direction to add the offset to\n * @param out The {@link Point} to add the offset to\n */\nexport function addDirectionalOffset(\n amount: number,\n direction: LinkDirection,\n out: Point\n): void {\n switch (direction) {\n case LinkDirection.LEFT:\n out[0] -= amount\n return\n case LinkDirection.RIGHT:\n out[0] += amount\n return\n case LinkDirection.UP:\n out[1] -= amount\n return\n case LinkDirection.DOWN:\n out[1] += amount\n return\n // LinkDirection.CENTER: Nothing to do.\n }\n}\n\n/**\n * Rotates an offset in 90° increments.\n *\n * Swaps/flips axis values of a 2D vector offset - effectively rotating\n * {@link offset} by 90°\n * @param offset The zero-based offset to rotate\n * @param from Direction to rotate from\n * @param to Direction to rotate to\n */\nexport function rotateLink(\n offset: Point,\n from: LinkDirection,\n to: LinkDirection\n): void {\n let x: number\n let y: number\n\n // Normalise to left\n switch (from) {\n case to:\n case LinkDirection.CENTER:\n case LinkDirection.NONE:\n default:\n // Nothing to do\n return\n\n case LinkDirection.LEFT:\n x = offset[0]\n y = offset[1]\n break\n case LinkDirection.RIGHT:\n x = -offset[0]\n y = -offset[1]\n break\n case LinkDirection.UP:\n x = -offset[1]\n y = offset[0]\n break\n case LinkDirection.DOWN:\n x = offset[1]\n y = -offset[0]\n break\n }\n\n // Apply new direction\n switch (to) {\n case LinkDirection.CENTER:\n case LinkDirection.NONE:\n // Nothing to do\n return\n\n case LinkDirection.LEFT:\n offset[0] = x\n offset[1] = y\n break\n case LinkDirection.RIGHT:\n offset[0] = -x\n offset[1] = -y\n break\n case LinkDirection.UP:\n offset[0] = y\n offset[1] = -x\n break\n case LinkDirection.DOWN:\n offset[0] = -y\n offset[1] = x\n break\n }\n}\n\n/**\n * Check if a point is to to the left or right of a line.\n * Project a line from lineStart -> lineEnd. Determine if point is to the left\n * or right of that projection.\n * {@link https://www.geeksforgeeks.org/orientation-3-ordered-points/}\n * @param lineStart The start point of the line\n * @param lineEnd The end point of the line\n * @param x X co-ordinate of the point to check\n * @param y Y co-ordinate of the point to check\n * @returns 0 if all three points are in a straight line, a negative value if\n * point is to the left of the projected line, or positive if the point is to\n * the right\n */\nexport function getOrientation(\n lineStart: Readonly<Point>,\n lineEnd: Readonly<Point>,\n x: number,\n y: number\n): number {\n return (\n (lineEnd[1] - lineStart[1]) * (x - lineEnd[0]) -\n (lineEnd[0] - lineStart[0]) * (y - lineEnd[1])\n )\n}\n\n/**\n * @param out The array to store the point in\n * @param a Start point\n * @param b End point\n * @param controlA Start curve control point\n * @param controlB End curve control point\n * @param t Time: factor of distance to travel along the curve (e.g 0.25 is 25% along the curve)\n */\nexport function findPointOnCurve(\n out: Point,\n a: Readonly<Point>,\n b: Readonly<Point>,\n controlA: Readonly<Point>,\n controlB: Readonly<Point>,\n t: number = 0.5\n): void {\n const iT = 1 - t\n\n const c1 = iT * iT * iT\n const c2 = 3 * (iT * iT) * t\n const c3 = 3 * iT * (t * t)\n const c4 = t * t * t\n\n out[0] = c1 * a[0] + c2 * controlA[0] + c3 * controlB[0] + c4 * b[0]\n out[1] = c1 * a[1] + c2 * controlA[1] + c3 * controlB[1] + c4 * b[1]\n}\n\nexport function createBounds(\n objects: Iterable<HasBoundingRect>,\n padding: number = 10\n): ReadOnlyRect | null {\n const bounds: Rect = [Infinity, Infinity, -Infinity, -Infinity]\n\n for (const obj of objects) {\n const rect = obj.boundingRect\n bounds[0] = Math.min(bounds[0], rect[0])\n bounds[1] = Math.min(bounds[1], rect[1])\n bounds[2] = Math.max(bounds[2], rect[0] + rect[2])\n bounds[3] = Math.max(bounds[3], rect[1] + rect[3])\n }\n if (!bounds.every((x) => isFinite(x))) return null\n\n return [\n bounds[0] - padding,\n bounds[1] - padding,\n bounds[2] - bounds[0] + 2 * padding,\n bounds[3] - bounds[1] + 2 * padding\n ]\n}\n\n/**\n * Snaps the provided {@link Point} or {@link Rect} ({@link pos}) to a grid of size {@link snapTo}.\n * @param pos The point that will be snapped\n * @param snapTo The value to round up/down by (multiples thereof)\n * @returns `true` if snapTo is truthy, otherwise `false`\n * @remarks `NaN` propagates through this function and does not affect return value.\n */\nexport function snapPoint(pos: Point | Rect, snapTo: number): boolean {\n if (!snapTo) return false\n\n pos[0] = snapTo * Math.round(pos[0] / snapTo)\n pos[1] = snapTo * Math.round(pos[1] / snapTo)\n return true\n}\n\n/**\n * Aligns a {@link Rect} relative to the edges or centre of a {@link container} rectangle.\n *\n * With no {@link inset}, the element will be placed on the interior of the {@link container},\n * with their edges lined up on the {@link anchors}. A positive {@link inset} moves the element towards the centre,\n * negative will push it outside the {@link container}.\n * @param rect The bounding rect of the element to align.\n * If using the element's pos/size backing store, this function will move the element.\n * @param anchors The direction(s) to anchor the element to\n * @param container The rectangle inside which to align the element\n * @param inset Relative offset from each {@link anchors} edge, with positive always leading to the centre, as an `[x, y]` point\n * @returns The original {@link rect}, modified in place.\n */\nexport function alignToContainer(\n rect: Rect,\n anchors: Alignment,\n [containerX, containerY, containerWidth, containerHeight]: ReadOnlyRect,\n [insetX, insetY]: Readonly<Point> = [0, 0]\n): Rect {\n if (hasFlag(anchors, Alignment.Left)) {\n // Left\n rect[0] = containerX + insetX\n } else if (hasFlag(anchors, Alignment.Right)) {\n // Right\n rect[0] = containerX + containerWidth - insetX - rect[2]\n } else if (hasFlag(anchors, Alignment.Centre)) {\n // Horizontal centre\n rect[0] = containerX + containerWidth * 0.5 - rect[2] * 0.5\n }\n\n if (hasFlag(anchors, Alignment.Top)) {\n // Top\n rect[1] = containerY + insetY\n } else if (hasFlag(anchors, Alignment.Bottom)) {\n // Bottom\n rect[1] = containerY + containerHeight - insetY - rect[3]\n } else if (hasFlag(anchors, Alignment.Middle)) {\n // Vertical middle\n rect[1] = containerY + containerHeight * 0.5 - rect[3] * 0.5\n }\n return rect\n}\n\n/**\n * Aligns a {@link Rect} relative to the edges of {@link other}.\n *\n * With no {@link outset}, the element will be placed on the exterior of the {@link other},\n * with their edges lined up on the {@link anchors}. A positive {@link outset} moves the element away from the {@link other},\n * negative will push it inside the {@link other}.\n * @param rect The bounding rect of the element to align.\n * If using the element's pos/size backing store, this function will move the element.\n * @param anchors The direction(s) to anchor the element to\n * @param other The rectangle to align {@link rect} to\n * @param outset Relative offset from each {@link anchors} edge, with positive always moving away from the centre of the {@link other}, as an `[x, y]` point\n * @returns The original {@link rect}, modified in place.\n */\nexport function alignOutsideContainer(\n rect: Rect,\n anchors: Alignment,\n [otherX, otherY, otherWidth, otherHeight]: ReadOnlyRect,\n [outsetX, outsetY]: Readonly<Point> = [0, 0]\n): Rect {\n if (hasFlag(anchors, Alignment.Left)) {\n // Left\n rect[0] = otherX - outsetX - rect[2]\n } else if (hasFlag(anchors, Alignment.Right)) {\n // Right\n rect[0] = otherX + otherWidth + outsetX\n } else if (hasFlag(anchors, Alignment.Centre)) {\n // Horizontal centre\n rect[0] = otherX + otherWidth * 0.5 - rect[2] * 0.5\n }\n\n if (hasFlag(anchors, Alignment.Top)) {\n // Top\n rect[1] = otherY - outsetY - rect[3]\n } else if (hasFlag(anchors, Alignment.Bottom)) {\n // Bottom\n rect[1] = otherY + otherHeight + outsetY\n } else if (hasFlag(anchors, Alignment.Middle)) {\n // Vertical middle\n rect[1] = otherY + otherHeight * 0.5 - rect[3] * 0.5\n }\n return rect\n}\n","import { clamp } from 'es-toolkit/compat'\n\nimport type { Point, Rect } from './interfaces'\nimport type { LGraphCanvas } from './litegraph'\nimport { distance } from './measure'\n\n// used by some widgets to render a curve editor\n\nexport class CurveEditor {\n points: Point[]\n selected: number\n nearest: number\n size: Rect | null\n must_update: boolean\n margin: number\n _nearest?: number\n\n constructor(points: Point[]) {\n this.points = points\n this.selected = -1\n this.nearest = -1\n // stores last size used\n this.size = null\n this.must_update = true\n this.margin = 5\n }\n\n static sampleCurve(f: number, points: Point[]): number | undefined {\n if (!points) return\n\n for (let i = 0; i < points.length - 1; ++i) {\n const p = points[i]\n const pn = points[i + 1]\n if (pn[0] < f) continue\n\n const r = pn[0] - p[0]\n if (Math.abs(r) < 0.000_01) return p[1]\n\n const local_f = (f - p[0]) / r\n return p[1] * (1.0 - local_f) + pn[1] * local_f\n }\n return 0\n }\n\n draw(\n ctx: CanvasRenderingContext2D,\n size: Rect,\n // @ts-expect-error - LGraphCanvas parameter type needs fixing\n graphcanvas?: LGraphCanvas,\n background_color?: string,\n line_color?: string,\n inactive = false\n ): void {\n const points = this.points\n if (!points) return\n\n this.size = size\n const w = size[0] - this.margin * 2\n const h = size[1] - this.margin * 2\n\n line_color = line_color || '#666'\n\n ctx.save()\n ctx.translate(this.margin, this.margin)\n\n if (background_color) {\n ctx.fillStyle = '#111'\n ctx.fillRect(0, 0, w, h)\n ctx.fillStyle = '#222'\n ctx.fillRect(w * 0.5, 0, 1, h)\n ctx.strokeStyle = '#333'\n ctx.strokeRect(0, 0, w, h)\n }\n ctx.strokeStyle = line_color\n if (inactive) ctx.globalAlpha = 0.5\n ctx.beginPath()\n for (const p of points) {\n ctx.lineTo(p[0] * w, (1.0 - p[1]) * h)\n }\n ctx.stroke()\n ctx.globalAlpha = 1\n if (!inactive) {\n for (const [i, p] of points.entries()) {\n ctx.fillStyle =\n this.selected == i ? '#FFF' : this.nearest == i ? '#DDD' : '#AAA'\n ctx.beginPath()\n ctx.arc(p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2)\n ctx.fill()\n }\n }\n ctx.restore()\n }\n\n // localpos is mouse in curve editor space\n onMouseDown(localpos: Point, graphcanvas: LGraphCanvas): boolean | undefined {\n const points = this.points\n if (!points) return\n if (localpos[1] < 0) return\n\n // this.captureInput(true);\n if (this.size == null)\n throw new Error('CurveEditor.size was null or undefined.')\n const w = this.size[0] - this.margin * 2\n const h = this.size[1] - this.margin * 2\n const x = localpos[0] - this.margin\n const y = localpos[1] - this.margin\n const pos: Point = [x, y]\n const max_dist = 30 / graphcanvas.ds.scale\n // search closer one\n this.selected = this.getCloserPoint(pos, max_dist)\n // create one\n if (this.selected == -1) {\n const point: Point = [x / w, 1 - y / h]\n points.push(point)\n points.sort(function (a, b) {\n return a[0] - b[0]\n })\n this.selected = points.indexOf(point)\n this.must_update = true\n }\n if (this.selected != -1) return true\n }\n\n onMouseMove(localpos: Point, graphcanvas: LGraphCanvas): void {\n const points = this.points\n if (!points) return\n\n const s = this.selected\n if (s < 0) return\n\n if (this.size == null)\n throw new Error('CurveEditor.size was null or undefined.')\n const x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2)\n const y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2)\n const curvepos: Point = [\n localpos[0] - this.margin,\n localpos[1] - this.margin\n ]\n const max_dist = 30 / graphcanvas.ds.scale\n this._nearest = this.getCloserPoint(curvepos, max_dist)\n const point = points[s]\n if (point) {\n const is_edge_point = s == 0 || s == points.length - 1\n if (\n !is_edge_point &&\n (localpos[0] < -10 ||\n localpos[0] > this.size[0] + 10 ||\n localpos[1] < -10 ||\n localpos[1] > this.size[1] + 10)\n ) {\n points.splice(s, 1)\n this.selected = -1\n return\n }\n // not edges\n if (!is_edge_point) point[0] = clamp(x, 0, 1)\n else point[0] = s == 0 ? 0 : 1\n point[1] = 1.0 - clamp(y, 0, 1)\n points.sort(function (a, b) {\n return a[0] - b[0]\n })\n this.selected = points.indexOf(point)\n this.must_update = true\n }\n }\n\n // Former params: localpos, graphcanvas\n onMouseUp(): boolean {\n this.selected = -1\n return false\n }\n\n getCloserPoint(pos: Point, max_dist: number): number {\n const points = this.points\n if (!points) return -1\n\n max_dist = max_dist || 30\n if (this.size == null)\n throw new Error('CurveEditor.size was null or undefined.')\n const w = this.size[0] - this.margin * 2\n const h = this.size[1] - this.margin * 2\n const num = points.length\n const p2: Point = [0, 0]\n let min_dist = 1_000_000\n let closest = -1\n\n for (let i = 0; i < num; ++i) {\n const p = points[i]\n p2[0] = p[0] * w\n p2[1] = (1.0 - p[1]) * h\n const dist = distance(pos, p2)\n if (dist > min_dist || dist > max_dist) continue\n\n closest = i\n min_dist = dist\n }\n return closest\n }\n}\n","import type { Point, ReadOnlyRect, Rect } from './interfaces'\nimport { EaseFunction, Rectangle } from './litegraph'\n\nexport interface DragAndScaleState {\n /**\n * The offset from the top-left of the current canvas viewport to `[0, 0]` in graph space.\n * Or said another way, the inverse offset of the viewport.\n */\n offset: [number, number]\n /** The scale of the graph. */\n scale: number\n}\n\nexport type AnimationOptions = {\n /** Duration of the animation in milliseconds. */\n duration?: number\n /** Relative target zoom level. 1 means the view is fit exactly on the bounding box. */\n zoom?: number\n /** The animation easing function (curve) */\n easing?: EaseFunction\n}\n\nexport class DragAndScale {\n /**\n * The state of this DragAndScale instance.\n *\n * Implemented as a POCO that can be proxied without side-effects.\n */\n state: DragAndScaleState\n lastState: DragAndScaleState = {\n offset: [0, 0],\n scale: 0\n }\n\n /** Maximum scale (zoom in) */\n max_scale: number\n /** Minimum scale (zoom out) */\n min_scale: number\n enabled: boolean\n last_mouse: Point\n element: HTMLCanvasElement\n visible_area: Rectangle\n dragging?: boolean\n viewport?: Rect\n\n onredraw?(das: DragAndScale): void\n onChanged?(scale: number, offset: Point): void\n\n get offset(): [number, number] {\n return this.state.offset\n }\n\n set offset(value: Point) {\n this.state.offset[0] = value[0]\n this.state.offset[1] = value[1]\n }\n\n get scale(): number {\n return this.state.scale\n }\n\n set scale(value: number) {\n this.state.scale = value\n }\n\n constructor(element: HTMLCanvasElement) {\n this.state = {\n offset: [0, 0],\n scale: 1\n }\n this.max_scale = 10\n this.min_scale = 0.1\n this.enabled = true\n this.last_mouse = [0, 0]\n this.visible_area = new Rectangle()\n\n this.element = element\n }\n\n /**\n * Returns `true` if the current state has changed from the previous state.\n * @returns `true` if the current state has changed from the previous state, otherwise `false`.\n */\n #stateHasChanged(): boolean {\n const current = this.state\n const previous = this.lastState\n\n return (\n current.scale !== previous.scale ||\n current.offset[0] !== previous.offset[0] ||\n current.offset[1] !== previous.offset[1]\n )\n }\n\n computeVisibleArea(viewport: Rect | undefined): void {\n const { scale, offset, visible_area } = this\n\n if (this.#stateHasChanged()) {\n this.onChanged?.(scale, offset)\n copyState(this.state, this.lastState)\n }\n\n if (!this.element) {\n visible_area[0] = visible_area[1] = visible_area[2] = visible_area[3] = 0\n return\n }\n let { width, height } = this.element\n let startx = -offset[0]\n let starty = -offset[1]\n if (viewport) {\n startx += viewport[0] / scale\n starty += viewport[1] / scale\n width = viewport[2]\n height = viewport[3]\n }\n const endx = startx + width / scale\n const endy = starty + height / scale\n visible_area[0] = startx\n visible_area[1] = starty\n visible_area.resizeBottomRight(endx, endy)\n }\n\n toCanvasContext(ctx: CanvasRenderingContext2D): void {\n ctx.scale(this.scale, this.scale)\n ctx.translate(this.offset[0], this.offset[1])\n }\n\n convertOffsetToCanvas(pos: Point): Point {\n return [\n (pos[0] + this.offset[0]) * this.scale,\n (pos[1] + this.offset[1]) * this.scale\n ]\n }\n\n convertCanvasToOffset(pos: Point, out?: Point): Point {\n out = out || [0, 0]\n out[0] = pos[0] / this.scale - this.offset[0]\n out[1] = pos[1] / this.scale - this.offset[1]\n return out\n }\n\n /** @deprecated Has not been kept up to date */\n mouseDrag(x: number, y: number): void {\n this.offset[0] += x / this.scale\n this.offset[1] += y / this.scale\n\n this.onredraw?.(this)\n }\n\n changeScale(\n value: number,\n zooming_center?: Point,\n roundToScaleOne = true\n ): void {\n if (value < this.min_scale) {\n value = this.min_scale\n } else if (value > this.max_scale) {\n value = this.max_scale\n }\n if (value == this.scale) return\n\n const rect = this.element.getBoundingClientRect()\n if (!rect) return\n\n zooming_center = zooming_center ?? [rect.width * 0.5, rect.height * 0.5]\n\n const normalizedCenter: Point = [\n zooming_center[0] - rect.x,\n zooming_center[1] - rect.y\n ]\n const center = this.convertCanvasToOffset(normalizedCenter)\n this.scale = value\n if (roundToScaleOne && Math.abs(this.scale - 1) < 0.01) this.scale = 1\n const new_center = this.convertCanvasToOffset(normalizedCenter)\n const delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]\n\n this.offset[0] += delta_offset[0]\n this.offset[1] += delta_offset[1]\n\n this.onredraw?.(this)\n }\n\n changeDeltaScale(value: number, zooming_center?: Point): void {\n this.changeScale(this.scale * value, zooming_center)\n }\n\n /**\n * Fits the view to the specified bounds.\n * @param bounds The bounds to fit the view to, defined by a rectangle.\n */\n fitToBounds(\n bounds: ReadOnlyRect,\n { zoom = 0.75 }: { zoom?: number } = {}\n ): void {\n //If element hasn't initialized (browser tab is in background)\n //it has a size of 300x150 and a more reasonable default is used instead.\n const [width, height] =\n this.element.width === 300 && this.element.height === 150\n ? [1920, 1080]\n : [this.element.width, this.element.height]\n const cw = width / window.devicePixelRatio\n const ch = height / window.devicePixelRatio\n let targetScale = this.scale\n\n if (zoom > 0) {\n const targetScaleX = (zoom * cw) / Math.max(bounds[2], 300)\n const targetScaleY = (zoom * ch) / Math.max(bounds[3], 300)\n\n // Choose the smaller scale to ensure the node fits into the viewport\n // Ensure we don't go over the max scale\n targetScale = Math.min(targetScaleX, targetScaleY, this.max_scale)\n }\n\n const scaledWidth = cw / targetScale\n const scaledHeight = ch / targetScale\n\n // Calculate the target position to center the bounds in the viewport\n const targetX = -bounds[0] - bounds[2] * 0.5 + scaledWidth * 0.5\n const targetY = -bounds[1] - bounds[3] * 0.5 + scaledHeight * 0.5\n\n // Apply the changes immediately\n this.offset[0] = targetX\n this.offset[1] = targetY\n this.scale = targetScale\n }\n\n /**\n * Starts an animation to fit the view around the specified selection of nodes.\n * @param bounds The bounds to animate the view to, defined by a rectangle.\n */\n animateToBounds(\n bounds: ReadOnlyRect,\n setDirty: () => void,\n {\n duration = 350,\n zoom = 0.75,\n easing = EaseFunction.EASE_IN_OUT_QUAD\n }: AnimationOptions = {}\n ) {\n if (!(duration > 0)) throw new RangeError('Duration must be greater than 0')\n\n const easeFunctions = {\n linear: (t: number) => t,\n easeInQuad: (t: number) => t * t,\n easeOutQuad: (t: number) => t * (2 - t),\n easeInOutQuad: (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)\n }\n const easeFunction = easeFunctions[easing] ?? easeFunctions.linear\n\n const startTimestamp = performance.now()\n const cw = this.element.width / window.devicePixelRatio\n const ch = this.element.height / window.devicePixelRatio\n const startX = this.offset[0]\n const startY = this.offset[1]\n const startX2 = startX - cw / this.scale\n const startY2 = startY - ch / this.scale\n const startScale = this.scale\n let targetScale = startScale\n\n if (zoom > 0) {\n const targetScaleX = (zoom * cw) / Math.max(bounds[2], 300)\n const targetScaleY = (zoom * ch) / Math.max(bounds[3], 300)\n\n // Choose the smaller scale to ensure the node fits into the viewport\n // Ensure we don't go over the max scale\n targetScale = Math.min(targetScaleX, targetScaleY, this.max_scale)\n }\n const scaledWidth = cw / targetScale\n const scaledHeight = ch / targetScale\n\n const targetX = -bounds[0] - bounds[2] * 0.5 + scaledWidth * 0.5\n const targetY = -bounds[1] - bounds[3] * 0.5 + scaledHeight * 0.5\n const targetX2 = targetX - scaledWidth\n const targetY2 = targetY - scaledHeight\n\n const animate = (timestamp: number) => {\n const elapsed = timestamp - startTimestamp\n const progress = Math.min(elapsed / duration, 1)\n const easedProgress = easeFunction(progress)\n\n const currentX = startX + (targetX - startX) * easedProgress\n const currentY = startY + (targetY - startY) * easedProgress\n this.offset[0] = currentX\n this.offset[1] = currentY\n\n if (zoom > 0) {\n const currentX2 = startX2 + (targetX2 - startX2) * easedProgress\n const currentY2 = startY2 + (targetY2 - startY2) * easedProgress\n const currentWidth = Math.abs(currentX2 - currentX)\n const currentHeight = Math.abs(currentY2 - currentY)\n\n this.scale = Math.min(cw / currentWidth, ch / currentHeight)\n }\n\n setDirty()\n\n if (progress < 1) {\n animationId = requestAnimationFrame(animate)\n } else {\n cancelAnimationFrame(animationId)\n }\n }\n let animationId = requestAnimationFrame(animate)\n }\n\n reset(): void {\n this.scale = 1\n this.offset[0] = 0\n this.offset[1] = 0\n }\n}\n\n/**\n * Copies the values of one state into another, preserving references.\n * @param from The state to copy values from.\n * @param to The state to copy values into.\n */\nfunction copyState(from: DragAndScaleState, to: DragAndScaleState): void {\n to.scale = from.scale\n to.offset[0] = from.offset[0]\n to.offset[1] = from.offset[1]\n}\n","// Using a template string for this is resulting in complex type workarounds. No current benefit beyond dev reading.\nexport type UUID = string\n\n/** Special-case zero-UUID, consisting entirely of zeros. Used as a default value. */\nexport const zeroUuid = '00000000-0000-0000-0000-000000000000'\n\n/** Pre-allocated storage for uuid random values. */\nconst randomStorage = new Uint32Array(31)\n\n/**\n * Creates a UUIDv4 string.\n * @returns A new UUIDv4 string\n * @remarks\n * Original implementation from https://gist.github.com/jed/982883?permalink_comment_id=852670#gistcomment-852670\n *\n * Prefers the {@link crypto.randomUUID} method if available, falling back to\n * {@link crypto.getRandomValues}, then finally the legacy {@link Math.random} method.\n */\nexport function createUuidv4(): UUID {\n if (typeof crypto?.randomUUID === 'function') return crypto.randomUUID()\n if (typeof crypto?.getRandomValues === 'function') {\n const random = crypto.getRandomValues(randomStorage)\n let i = 0\n return '10000000-1000-4000-8000-100000000000'.replaceAll(/[018]/g, (a) =>\n (\n Number(a) ^\n ((random[i++] * 3.725_290_298_461_914e-9) >> (Number(a) * 0.25))\n ).toString(16)\n )\n }\n return '10000000-1000-4000-8000-100000000000'.replaceAll(/[018]/g, (a) =>\n (Number(a) ^ ((Math.random() * 16) >> (Number(a) * 0.25))).toString(16)\n )\n}\n","import { LiteGraph } from '@/lib/litegraph/src/litegraph'\n\nexport const removeNodeTitleHeight = (height: number) =>\n Math.max(0, height - (LiteGraph.NODE_TITLE_HEIGHT || 0))\n","/**\n * Layout System - Type Definitions\n *\n * This file contains all type definitions for the layout system\n * that manages node positions, bounds, spatial data, and operations.\n */\nimport type { ComputedRef, Ref } from 'vue'\n\n// Enum for layout source types\nexport enum LayoutSource {\n Canvas = 'canvas',\n Vue = 'vue',\n DOM = 'dom',\n External = 'external'\n}\n\n// Basic geometric types\nexport interface Point {\n x: number\n y: number\n}\n\nexport interface Size {\n width: number\n height: number\n}\n\nexport interface Bounds {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface NodeBoundsUpdate {\n nodeId: NodeId\n bounds: Bounds\n}\n\nexport type NodeId = string\nexport type LinkId = number\nexport type RerouteId = number\n\n// Layout data structures\nexport interface NodeLayout {\n id: NodeId\n position: Point\n size: Size\n zIndex: number\n visible: boolean\n // Computed bounds for hit testing\n bounds: Bounds\n}\n\nexport interface SlotLayout {\n nodeId: NodeId\n index: number\n type: 'input' | 'output'\n position: Point\n bounds: Bounds\n}\n\nexport interface LinkLayout {\n id: LinkId\n path: Path2D\n bounds: Bounds\n centerPos: Point\n sourceNodeId: NodeId\n targetNodeId: NodeId\n sourceSlot: number\n targetSlot: number\n}\n\n// Layout for individual link segments (for precise hit-testing)\nexport interface LinkSegmentLayout {\n linkId: LinkId\n rerouteId: RerouteId | null // null for final segment to target\n path: Path2D\n bounds: Bounds\n centerPos: Point\n}\n\nexport interface RerouteLayout {\n id: RerouteId\n position: Point\n radius: number\n bounds: Bounds\n}\n\n/**\n * Meta-only base for all operations - contains common fields\n */\ninterface OperationMeta {\n /** Unique operation ID for deduplication */\n id?: string\n /** Timestamp for ordering operations */\n timestamp: number\n /** Actor who performed the operation (for CRDT) */\n actor: string\n /** Source system that initiated the operation */\n source: LayoutSource\n /** Operation type discriminator */\n type: OperationType\n}\n\n/**\n * Entity-specific base types for proper type discrimination\n */\ntype NodeOpBase = OperationMeta & { entity: 'node'; nodeId: NodeId }\ntype LinkOpBase = OperationMeta & { entity: 'link'; linkId: LinkId }\ntype RerouteOpBase = OperationMeta & {\n entity: 'reroute'\n rerouteId: RerouteId\n}\n\n/**\n * Operation type discriminator for type narrowing\n */\ntype OperationType =\n | 'moveNode'\n | 'resizeNode'\n | 'setNodeZIndex'\n | 'createNode'\n | 'deleteNode'\n | 'setNodeVisibility'\n | 'batchUpdateBounds'\n | 'createLink'\n | 'deleteLink'\n | 'createReroute'\n | 'deleteReroute'\n | 'moveReroute'\n\n/**\n * Move node operation\n */\nexport interface MoveNodeOperation extends NodeOpBase {\n type: 'moveNode'\n position: Point\n previousPosition: Point\n}\n\n/**\n * Resize node operation\n */\nexport interface ResizeNodeOperation extends NodeOpBase {\n type: 'resizeNode'\n size: { width: number; height: number }\n previousSize: { width: number; height: number }\n}\n\n/**\n * Set node z-index operation\n */\nexport interface SetNodeZIndexOperation extends NodeOpBase {\n type: 'setNodeZIndex'\n zIndex: number\n previousZIndex: number\n}\n\n/**\n * Create node operation\n */\nexport interface CreateNodeOperation extends NodeOpBase {\n type: 'createNode'\n layout: NodeLayout\n}\n\n/**\n * Delete node operation\n */\nexport interface DeleteNodeOperation extends NodeOpBase {\n type: 'deleteNode'\n previousLayout: NodeLayout\n}\n\n/**\n * Set node visibility operation\n */\ninterface SetNodeVisibilityOperation extends NodeOpBase {\n type: 'setNodeVisibility'\n visible: boolean\n previousVisible: boolean\n}\n\n/**\n * Batch update operation for atomic multi-property changes\n */\nexport interface BatchUpdateBoundsOperation extends OperationMeta {\n entity: 'node'\n type: 'batchUpdateBounds'\n nodeIds: NodeId[]\n bounds: Record<NodeId, { bounds: Bounds; previousBounds: Bounds }>\n}\n\n/**\n * Create link operation\n */\nexport interface CreateLinkOperation extends LinkOpBase {\n type: 'createLink'\n sourceNodeId: NodeId\n sourceSlot: number\n targetNodeId: NodeId\n targetSlot: number\n}\n\n/**\n * Delete link operation\n */\nexport interface DeleteLinkOperation extends LinkOpBase {\n type: 'deleteLink'\n}\n\n/**\n * Create reroute operation\n */\nexport interface CreateRerouteOperation extends RerouteOpBase {\n type: 'createReroute'\n position: Point\n parentId?: RerouteId\n linkIds: LinkId[]\n}\n\n/**\n * Delete reroute operation\n */\nexport interface DeleteRerouteOperation extends RerouteOpBase {\n type: 'deleteReroute'\n}\n\n/**\n * Move reroute operation\n */\nexport interface MoveRerouteOperation extends RerouteOpBase {\n type: 'moveReroute'\n position: Point\n previousPosition: Point\n}\n\n/**\n * Union of all operation types\n */\nexport type LayoutOperation =\n | MoveNodeOperation\n | ResizeNodeOperation\n | SetNodeZIndexOperation\n | CreateNodeOperation\n | DeleteNodeOperation\n | SetNodeVisibilityOperation\n | BatchUpdateBoundsOperation\n | CreateLinkOperation\n | DeleteLinkOperation\n | CreateRerouteOperation\n | DeleteRerouteOperation\n | MoveRerouteOperation\n\nexport interface LayoutChange {\n type: 'create' | 'update' | 'delete'\n nodeIds: NodeId[]\n timestamp: number\n source: LayoutSource\n operation: LayoutOperation\n}\n\n// Store interfaces\nexport interface LayoutStore {\n // CustomRef accessors for shared write access\n getNodeLayoutRef(nodeId: NodeId): Ref<NodeLayout | null>\n getNodesInBounds(bounds: Bounds): ComputedRef<NodeId[]>\n getAllNodes(): ComputedRef<ReadonlyMap<NodeId, NodeLayout>>\n getVersion(): ComputedRef<number>\n\n // Spatial queries (non-reactive)\n queryNodeAtPoint(point: Point): NodeId | null\n queryNodesInBounds(bounds: Bounds): NodeId[]\n\n // Hit testing queries for links, slots, and reroutes\n queryLinkAtPoint(point: Point, ctx?: CanvasRenderingContext2D): LinkId | null\n queryLinkSegmentAtPoint(\n point: Point,\n ctx?: CanvasRenderingContext2D\n ): { linkId: LinkId; rerouteId: RerouteId | null } | null\n querySlotAtPoint(point: Point): SlotLayout | null\n queryRerouteAtPoint(point: Point): RerouteLayout | null\n queryItemsInBounds(bounds: Bounds): {\n nodes: NodeId[]\n links: LinkId[]\n slots: string[]\n reroutes: RerouteId[]\n }\n\n // Update methods for link, slot, and reroute layouts\n updateLinkLayout(linkId: LinkId, layout: LinkLayout): void\n updateLinkSegmentLayout(\n linkId: LinkId,\n rerouteId: RerouteId | null,\n layout: Omit<LinkSegmentLayout, 'linkId' | 'rerouteId'>\n ): void\n updateSlotLayout(key: string, layout: SlotLayout): void\n updateRerouteLayout(rerouteId: RerouteId, layout: RerouteLayout): void\n\n // Delete methods for cleanup\n deleteLinkLayout(linkId: LinkId): void\n deleteLinkSegmentLayout(linkId: LinkId, rerouteId: RerouteId | null): void\n deleteSlotLayout(key: string): void\n deleteNodeSlotLayouts(nodeId: NodeId): void\n deleteRerouteLayout(rerouteId: RerouteId): void\n clearAllSlotLayouts(): void\n\n // Get layout data\n getLinkLayout(linkId: LinkId): LinkLayout | null\n getSlotLayout(key: string): SlotLayout | null\n getRerouteLayout(rerouteId: RerouteId): RerouteLayout | null\n\n // Returns all slot layout keys currently tracked by the store\n getAllSlotKeys(): string[]\n\n // Direct mutation API (CRDT-ready)\n applyOperation(operation: LayoutOperation): void\n\n // Change subscription\n onChange(callback: (change: LayoutChange) => void): () => void\n\n // Initialization\n initializeFromLiteGraph(\n nodes: Array<{ id: string; pos: [number, number]; size: [number, number] }>\n ): void\n\n // Source and actor management\n setSource(source: LayoutSource): void\n setActor(actor: string): void\n getCurrentSource(): LayoutSource\n getCurrentActor(): string\n\n // Batch updates\n batchUpdateNodeBounds(\n updates: Array<{ nodeId: NodeId; bounds: Bounds }>\n ): void\n\n batchUpdateSlotLayouts(\n updates: Array<{ key: string; layout: SlotLayout }>\n ): void\n}\n","/**\n * Layout System Constants\n *\n * Centralized configuration values for the layout system.\n * These values control spatial indexing, performance, and behavior.\n */\nimport { LayoutSource } from '@/renderer/core/layout/types'\n\n/**\n * QuadTree configuration for spatial indexing\n */\nexport const QUADTREE_CONFIG = {\n /** Default bounds for the QuadTree - covers a large canvas area */\n DEFAULT_BOUNDS: {\n x: -10000,\n y: -10000,\n width: 20000,\n height: 20000\n },\n /** Maximum tree depth to prevent excessive subdivision */\n MAX_DEPTH: 6,\n /** Maximum items per node before subdivision */\n MAX_ITEMS_PER_NODE: 4\n} as const\n\n/**\n * Performance and optimization settings\n */\nexport const PERFORMANCE_CONFIG = {\n /** RAF-based change detection interval (roughly 60fps) */\n CHANGE_DETECTION_INTERVAL: 16,\n /** Spatial query cache TTL in milliseconds */\n SPATIAL_CACHE_TTL: 1000,\n /** Maximum cache size for spatial queries */\n SPATIAL_CACHE_MAX_SIZE: 100,\n /** Batch update delay in milliseconds */\n BATCH_UPDATE_DELAY: 4\n} as const\n\n/**\n * Actor and source identifiers\n */\nexport const ACTOR_CONFIG = {\n /** Prefix for auto-generated actor IDs */\n USER_PREFIX: 'user-',\n /** Length of random suffix for actor IDs */\n ID_LENGTH: 9,\n /** Default source when not specified */\n DEFAULT_SOURCE: LayoutSource.External\n} as const\n","import type { Bounds, Point, Size } from '@/renderer/core/layout/types'\n\nexport function toPoint(x: number, y: number): Point {\n return { x, y }\n}\n\nexport function isPointEqual(a: Point, b: Point): boolean {\n return a.x === b.x && a.y === b.y\n}\n\nexport function isBoundsEqual(a: Bounds, b: Bounds): boolean {\n return (\n a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height\n )\n}\n\nexport function isSizeEqual(a: Size, b: Size): boolean {\n return a.width === b.width && a.height === b.height\n}\n","import type { Bounds, NodeLayout, Point } from '@/renderer/core/layout/types'\n\nexport const REROUTE_RADIUS = 8\n\nexport function pointInBounds(point: Point, bounds: Bounds): boolean {\n return (\n point.x >= bounds.x &&\n point.x <= bounds.x + bounds.width &&\n point.y >= bounds.y &&\n point.y <= bounds.y + bounds.height\n )\n}\n\nexport function boundsIntersect(a: Bounds, b: Bounds): boolean {\n return !(\n a.x + a.width < b.x ||\n b.x + b.width < a.x ||\n a.y + a.height < b.y ||\n b.y + b.height < a.y\n )\n}\n\nexport function calculateBounds(nodes: NodeLayout[]): Bounds {\n let minX = Infinity,\n minY = Infinity\n let maxX = -Infinity,\n maxY = -Infinity\n\n for (const node of nodes) {\n const bounds = node.bounds\n minX = Math.min(minX, bounds.x)\n minY = Math.min(minY, bounds.y)\n maxX = Math.max(maxX, bounds.x + bounds.width)\n maxY = Math.max(maxY, bounds.y + bounds.height)\n }\n\n return {\n x: minX,\n y: minY,\n width: maxX - minX,\n height: maxY - minY\n }\n}\n","import type { LinkId, RerouteId } from '@/renderer/core/layout/types'\n\n/**\n * Creates a unique key for identifying link segments in spatial indexes\n */\nexport function makeLinkSegmentKey(\n linkId: LinkId,\n rerouteId: RerouteId | null\n): string {\n return `${linkId}:${rerouteId ?? 'final'}`\n}\n","import * as Y from 'yjs'\n\nimport type { NodeLayout } from '@/renderer/core/layout/types'\n\nexport type NodeLayoutMap = Y.Map<NodeLayout[keyof NodeLayout]>\n\nexport const NODE_LAYOUT_DEFAULTS: NodeLayout = {\n id: 'unknown-node',\n position: { x: 0, y: 0 },\n size: { width: 100, height: 50 },\n zIndex: 0,\n visible: true,\n bounds: { x: 0, y: 0, width: 100, height: 50 }\n}\n\nexport function layoutToYNode(layout: NodeLayout): NodeLayoutMap {\n const ynode = new Y.Map<NodeLayout[keyof NodeLayout]>() as NodeLayoutMap\n ynode.set('id', layout.id)\n ynode.set('position', layout.position)\n ynode.set('size', layout.size)\n ynode.set('zIndex', layout.zIndex)\n ynode.set('visible', layout.visible)\n ynode.set('bounds', layout.bounds)\n return ynode\n}\n\nfunction getOr<K extends keyof NodeLayout>(\n map: NodeLayoutMap,\n key: K,\n fallback: NodeLayout[K]\n): NodeLayout[K] {\n const v = map.get(key)\n return (v ?? fallback) as NodeLayout[K]\n}\n\nexport function yNodeToLayout(ynode: NodeLayoutMap): NodeLayout {\n return {\n id: getOr(ynode, 'id', NODE_LAYOUT_DEFAULTS.id),\n position: getOr(ynode, 'position', NODE_LAYOUT_DEFAULTS.position),\n size: getOr(ynode, 'size', NODE_LAYOUT_DEFAULTS.size),\n zIndex: getOr(ynode, 'zIndex', NODE_LAYOUT_DEFAULTS.zIndex),\n visible: getOr(ynode, 'visible', NODE_LAYOUT_DEFAULTS.visible),\n bounds: getOr(ynode, 'bounds', NODE_LAYOUT_DEFAULTS.bounds)\n }\n}\n","/**\n * QuadTree implementation for spatial indexing of nodes\n * Optimized for viewport culling in large node graphs\n */\nimport type {\n QuadNodeDebugInfo,\n SpatialIndexDebugInfo\n} from '@/types/spatialIndex'\n\nexport interface Bounds {\n x: number\n y: number\n width: number\n height: number\n}\n\ninterface QuadTreeItem<T> {\n id: string\n bounds: Bounds\n data: T\n}\n\ninterface QuadTreeOptions {\n maxDepth?: number\n maxItemsPerNode?: number\n minNodeSize?: number\n}\n\nclass QuadNode<T> {\n private bounds: Bounds\n private depth: number\n private maxDepth: number\n private maxItems: number\n private items: QuadTreeItem<T>[] = []\n private children: QuadNode<T>[] | null = null\n private divided = false\n\n constructor(\n bounds: Bounds,\n depth: number = 0,\n maxDepth: number = 5,\n maxItems: number = 4\n ) {\n this.bounds = bounds\n this.depth = depth\n this.maxDepth = maxDepth\n this.maxItems = maxItems\n }\n\n insert(item: QuadTreeItem<T>): boolean {\n // Check if item is within bounds\n if (!this.contains(item.bounds)) {\n return false\n }\n\n // If we have space and haven't divided, add to this node\n if (this.items.length < this.maxItems && !this.divided) {\n this.items.push(item)\n return true\n }\n\n // If we haven't reached max depth, subdivide\n if (!this.divided && this.depth < this.maxDepth) {\n this.subdivide()\n }\n\n // If divided, insert into children\n if (this.divided && this.children) {\n for (const child of this.children) {\n if (child.insert(item)) {\n return true\n }\n }\n }\n\n // If we can't subdivide further, add to this node anyway\n this.items.push(item)\n return true\n }\n\n remove(item: QuadTreeItem<T>): boolean {\n const index = this.items.findIndex((i) => i.id === item.id)\n if (index !== -1) {\n this.items.splice(index, 1)\n return true\n }\n\n if (this.divided && this.children) {\n for (const child of this.children) {\n if (child.remove(item)) {\n return true\n }\n }\n }\n\n return false\n }\n\n query(\n searchBounds: Bounds,\n found: QuadTreeItem<T>[] = []\n ): QuadTreeItem<T>[] {\n // Check if search area intersects with this node\n if (!this.intersects(searchBounds)) {\n return found\n }\n\n // Add items in this node that intersect with search bounds\n for (const item of this.items) {\n if (this.boundsIntersect(item.bounds, searchBounds)) {\n found.push(item)\n }\n }\n\n // Recursively search children\n if (this.divided && this.children) {\n for (const child of this.children) {\n child.query(searchBounds, found)\n }\n }\n\n return found\n }\n\n private subdivide() {\n const { x, y, width, height } = this.bounds\n const halfWidth = width / 2\n const halfHeight = height / 2\n\n this.children = [\n // Top-left\n new QuadNode<T>(\n { x, y, width: halfWidth, height: halfHeight },\n this.depth + 1,\n this.maxDepth,\n this.maxItems\n ),\n // Top-right\n new QuadNode<T>(\n { x: x + halfWidth, y, width: halfWidth, height: halfHeight },\n this.depth + 1,\n this.maxDepth,\n this.maxItems\n ),\n // Bottom-left\n new QuadNode<T>(\n { x, y: y + halfHeight, width: halfWidth, height: halfHeight },\n this.depth + 1,\n this.maxDepth,\n this.maxItems\n ),\n // Bottom-right\n new QuadNode<T>(\n {\n x: x + halfWidth,\n y: y + halfHeight,\n width: halfWidth,\n height: halfHeight\n },\n this.depth + 1,\n this.maxDepth,\n this.maxItems\n )\n ]\n\n this.divided = true\n\n // Redistribute existing items to children\n const itemsToRedistribute = [...this.items]\n this.items = []\n\n for (const item of itemsToRedistribute) {\n let inserted = false\n for (const child of this.children) {\n if (child.insert(item)) {\n inserted = true\n break\n }\n }\n // Keep in parent if it doesn't fit in any child\n if (!inserted) {\n this.items.push(item)\n }\n }\n }\n\n private contains(itemBounds: Bounds): boolean {\n return (\n itemBounds.x >= this.bounds.x &&\n itemBounds.y >= this.bounds.y &&\n itemBounds.x + itemBounds.width <= this.bounds.x + this.bounds.width &&\n itemBounds.y + itemBounds.height <= this.bounds.y + this.bounds.height\n )\n }\n\n private intersects(searchBounds: Bounds): boolean {\n return this.boundsIntersect(this.bounds, searchBounds)\n }\n\n private boundsIntersect(a: Bounds, b: Bounds): boolean {\n return !(\n a.x + a.width < b.x ||\n b.x + b.width < a.x ||\n a.y + a.height < b.y ||\n b.y + b.height < a.y\n )\n }\n\n // Debug helper to get tree structure\n getDebugInfo(): QuadNodeDebugInfo {\n return {\n bounds: this.bounds,\n depth: this.depth,\n itemCount: this.items.length,\n divided: this.divided,\n children: this.children?.map((child) => child.getDebugInfo())\n }\n }\n}\n\nexport class QuadTree<T> {\n private root: QuadNode<T>\n private itemMap: Map<string, QuadTreeItem<T>> = new Map()\n private options: Required<QuadTreeOptions>\n\n constructor(bounds: Bounds, options: QuadTreeOptions = {}) {\n this.options = {\n maxDepth: options.maxDepth ?? 5,\n maxItemsPerNode: options.maxItemsPerNode ?? 4,\n minNodeSize: options.minNodeSize ?? 50\n }\n\n this.root = new QuadNode<T>(\n bounds,\n 0,\n this.options.maxDepth,\n this.options.maxItemsPerNode\n )\n }\n\n insert(id: string, bounds: Bounds, data: T): boolean {\n const item: QuadTreeItem<T> = { id, bounds, data }\n\n // Remove old item if it exists\n if (this.itemMap.has(id)) {\n this.remove(id)\n }\n\n const success = this.root.insert(item)\n if (success) {\n this.itemMap.set(id, item)\n }\n return success\n }\n\n remove(id: string): boolean {\n const item = this.itemMap.get(id)\n if (!item) return false\n\n const success = this.root.remove(item)\n if (success) {\n this.itemMap.delete(id)\n }\n return success\n }\n\n update(id: string, newBounds: Bounds): boolean {\n const item = this.itemMap.get(id)\n if (!item) return false\n\n // Remove and re-insert with new bounds\n const data = item.data\n this.remove(id)\n return this.insert(id, newBounds, data)\n }\n\n query(searchBounds: Bounds): T[] {\n const items = this.root.query(searchBounds)\n return items.map((item) => item.data)\n }\n\n clear() {\n this.root = new QuadNode<T>(\n this.root['bounds'],\n 0,\n this.options.maxDepth,\n this.options.maxItemsPerNode\n )\n this.itemMap.clear()\n }\n\n get size(): number {\n return this.itemMap.size\n }\n\n getDebugInfo(): SpatialIndexDebugInfo {\n return {\n size: this.size,\n tree: this.root.getDebugInfo()\n }\n }\n}\n","/**\n * Spatial Index Manager\n *\n * Manages spatial indexing for efficient node queries based on bounds.\n * Uses QuadTree for fast spatial lookups with caching for performance.\n */\nimport {\n PERFORMANCE_CONFIG,\n QUADTREE_CONFIG\n} from '@/renderer/core/layout/constants'\nimport type { Bounds, NodeId } from '@/renderer/core/layout/types'\n\nimport { QuadTree } from './QuadTree'\n\n/**\n * Cache entry for spatial queries\n */\ninterface CacheEntry {\n result: NodeId[]\n timestamp: number\n}\n\n/**\n * Spatial index manager using QuadTree\n */\nexport class SpatialIndexManager {\n private quadTree: QuadTree<NodeId>\n private queryCache: Map<string, CacheEntry>\n private cacheSize = 0\n\n constructor(bounds?: Bounds) {\n this.quadTree = new QuadTree<NodeId>(\n bounds ?? QUADTREE_CONFIG.DEFAULT_BOUNDS,\n {\n maxDepth: QUADTREE_CONFIG.MAX_DEPTH,\n maxItemsPerNode: QUADTREE_CONFIG.MAX_ITEMS_PER_NODE\n }\n )\n this.queryCache = new Map()\n }\n\n /**\n * Insert a node into the spatial index\n */\n insert(nodeId: NodeId, bounds: Bounds): void {\n this.quadTree.insert(nodeId, bounds, nodeId)\n this.invalidateCache()\n }\n\n /**\n * Update a node's bounds in the spatial index\n */\n update(nodeId: NodeId, bounds: Bounds): void {\n this.quadTree.update(nodeId, bounds)\n this.invalidateCache()\n }\n\n /**\n * Batch update multiple nodes' bounds in the spatial index\n * More efficient than calling update() multiple times as it only invalidates cache once\n */\n batchUpdate(updates: Array<{ nodeId: NodeId; bounds: Bounds }>): void {\n for (const { nodeId, bounds } of updates) {\n this.quadTree.update(nodeId, bounds)\n }\n this.invalidateCache()\n }\n\n /**\n * Remove a node from the spatial index\n */\n remove(nodeId: NodeId): void {\n this.quadTree.remove(nodeId)\n this.invalidateCache()\n }\n\n /**\n * Query nodes within the given bounds\n */\n query(bounds: Bounds): NodeId[] {\n const cacheKey = this.getCacheKey(bounds)\n const cached = this.queryCache.get(cacheKey)\n\n // Check cache validity\n if (cached) {\n const age = Date.now() - cached.timestamp\n if (age < PERFORMANCE_CONFIG.SPATIAL_CACHE_TTL) {\n return cached.result\n }\n // Remove stale entry\n this.queryCache.delete(cacheKey)\n this.cacheSize--\n }\n\n // Perform query\n const result = this.quadTree.query(bounds)\n\n // Cache result\n this.addToCache(cacheKey, result)\n\n return result\n }\n\n /**\n * Clear all nodes from the spatial index\n */\n clear(): void {\n this.quadTree.clear()\n this.invalidateCache()\n }\n\n /**\n * Get the current size of the index\n */\n get size(): number {\n return this.quadTree.size\n }\n\n /**\n * Get debug information about the spatial index\n */\n getDebugInfo() {\n return {\n quadTreeInfo: this.quadTree.getDebugInfo(),\n cacheSize: this.cacheSize,\n cacheEntries: this.queryCache.size\n }\n }\n\n /**\n * Generate cache key for bounds\n */\n private getCacheKey(bounds: Bounds): string {\n return `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`\n }\n\n /**\n * Add result to cache with LRU eviction\n */\n private addToCache(key: string, result: NodeId[]): void {\n // Evict oldest entries if cache is full\n if (this.cacheSize >= PERFORMANCE_CONFIG.SPATIAL_CACHE_MAX_SIZE) {\n const oldestKey = this.findOldestCacheEntry()\n if (oldestKey) {\n this.queryCache.delete(oldestKey)\n this.cacheSize--\n }\n }\n\n this.queryCache.set(key, {\n result,\n timestamp: Date.now()\n })\n this.cacheSize++\n }\n\n /**\n * Find oldest cache entry for LRU eviction\n */\n private findOldestCacheEntry(): string | null {\n let oldestKey: string | null = null\n let oldestTime = Infinity\n\n for (const [key, entry] of this.queryCache) {\n if (entry.timestamp < oldestTime) {\n oldestTime = entry.timestamp\n oldestKey = key\n }\n }\n\n return oldestKey\n }\n\n /**\n * Invalidate all cached queries\n */\n private invalidateCache(): void {\n this.queryCache.clear()\n this.cacheSize = 0\n }\n}\n","/**\n * Layout Store - Single Source of Truth\n *\n * Uses Yjs for efficient local state management and future collaboration.\n * CRDT ensures conflict-free operations for both single and multi-user scenarios.\n */\nimport log from 'loglevel'\nimport { computed, customRef, ref } from 'vue'\nimport type { ComputedRef, Ref } from 'vue'\nimport * as Y from 'yjs'\n\nimport { removeNodeTitleHeight } from '@/renderer/core/layout/utils/nodeSizeUtil'\n\nimport { ACTOR_CONFIG } from '@/renderer/core/layout/constants'\nimport { LayoutSource } from '@/renderer/core/layout/types'\nimport type {\n BatchUpdateBoundsOperation,\n Bounds,\n CreateLinkOperation,\n CreateNodeOperation,\n CreateRerouteOperation,\n DeleteLinkOperation,\n DeleteNodeOperation,\n DeleteRerouteOperation,\n LayoutChange,\n LayoutOperation,\n LayoutStore,\n LinkId,\n LinkLayout,\n LinkSegmentLayout,\n MoveNodeOperation,\n MoveRerouteOperation,\n NodeBoundsUpdate,\n NodeId,\n NodeLayout,\n Point,\n RerouteId,\n RerouteLayout,\n ResizeNodeOperation,\n SetNodeZIndexOperation,\n SlotLayout\n} from '@/renderer/core/layout/types'\nimport {\n isBoundsEqual,\n isPointEqual\n} from '@/renderer/core/layout/utils/geometry'\nimport {\n REROUTE_RADIUS,\n boundsIntersect,\n pointInBounds\n} from '@/renderer/core/layout/utils/layoutMath'\nimport { makeLinkSegmentKey } from '@/renderer/core/layout/utils/layoutUtils'\nimport {\n layoutToYNode,\n yNodeToLayout\n} from '@/renderer/core/layout/utils/mappers'\nimport type { NodeLayoutMap } from '@/renderer/core/layout/utils/mappers'\nimport { SpatialIndexManager } from '@/renderer/core/spatial/SpatialIndex'\n\ntype YEventChange = {\n action: 'add' | 'update' | 'delete'\n oldValue: unknown\n}\n\nconst logger = log.getLogger('LayoutStore')\n\n// Utility functions\nfunction asRerouteId(id: string | number): RerouteId {\n return Number(id)\n}\n\nfunction asLinkId(id: string | number): LinkId {\n return Number(id)\n}\n\ninterface LinkData {\n id: LinkId\n sourceNodeId: NodeId\n targetNodeId: NodeId\n sourceSlot: number\n targetSlot: number\n}\n\ninterface RerouteData {\n id: RerouteId\n position: Point\n parentId: LinkId\n linkIds: LinkId[]\n}\n\n// Generic typed Y.Map interface\ninterface TypedYMap<T> {\n get<K extends keyof T>(key: K): T[K] | undefined\n get<K extends keyof T>(key: K, defaultValue: T[K]): T[K]\n}\n\nclass LayoutStoreImpl implements LayoutStore {\n private static readonly REROUTE_DEFAULTS: RerouteData = {\n id: 0,\n position: { x: 0, y: 0 },\n parentId: 0,\n linkIds: []\n }\n\n // Yjs document and shared data structures\n private ydoc = new Y.Doc()\n private ynodes: Y.Map<NodeLayoutMap> // Maps nodeId -> NodeLayoutMap containing NodeLayout data\n private ylinks: Y.Map<Y.Map<unknown>> // Maps linkId -> Y.Map containing link data\n private yreroutes: Y.Map<Y.Map<unknown>> // Maps rerouteId -> Y.Map containing reroute data\n private yoperations: Y.Array<LayoutOperation> // Operation log\n\n // Vue reactivity layer\n private version = 0\n private currentSource: LayoutSource =\n ACTOR_CONFIG.DEFAULT_SOURCE as LayoutSource\n private currentActor = `${ACTOR_CONFIG.USER_PREFIX}${Math.random()\n .toString(36)\n .substring(2, 2 + ACTOR_CONFIG.ID_LENGTH)}`\n\n // Change listeners\n private changeListeners = new Set<(change: LayoutChange) => void>()\n\n // CustomRef cache and trigger functions\n private nodeRefs = new Map<NodeId, Ref<NodeLayout | null>>()\n private nodeTriggers = new Map<NodeId, () => void>()\n\n // New data structures for hit testing\n private linkLayouts = new Map<LinkId, LinkLayout>()\n private linkSegmentLayouts = new Map<string, LinkSegmentLayout>() // Internal string key: ${linkId}:${rerouteId ?? 'final'}\n private slotLayouts = new Map<string, SlotLayout>()\n private rerouteLayouts = new Map<RerouteId, RerouteLayout>()\n\n // Spatial index managers\n private spatialIndex: SpatialIndexManager // For nodes\n private linkSegmentSpatialIndex: SpatialIndexManager // For link segments (single index for all link geometry)\n private slotSpatialIndex: SpatialIndexManager // For slots\n private rerouteSpatialIndex: SpatialIndexManager // For reroutes\n\n // Vue dragging state for selection toolbox (public ref for direct mutation)\n public isDraggingVueNodes = ref(false)\n // Vue resizing state to prevent drag from activating during resize\n public isResizingVueNodes = ref(false)\n\n constructor() {\n // Initialize Yjs data structures\n this.ynodes = this.ydoc.getMap('nodes')\n this.ylinks = this.ydoc.getMap('links')\n this.yreroutes = this.ydoc.getMap('reroutes')\n this.yoperations = this.ydoc.getArray('operations')\n\n // Initialize spatial index managers\n this.spatialIndex = new SpatialIndexManager()\n this.linkSegmentSpatialIndex = new SpatialIndexManager() // Single index for all link geometry\n this.slotSpatialIndex = new SpatialIndexManager()\n this.rerouteSpatialIndex = new SpatialIndexManager()\n\n // Listen for Yjs changes and trigger Vue reactivity\n this.ynodes.observe((event: Y.YMapEvent<NodeLayoutMap>) => {\n this.version++\n\n // Trigger all affected node refs\n event.changes.keys.forEach((_change: YEventChange, key: string) => {\n const trigger = this.nodeTriggers.get(key)\n if (trigger) {\n trigger()\n }\n })\n })\n\n // Listen for link changes and update spatial indexes\n this.ylinks.observe((event: Y.YMapEvent<Y.Map<unknown>>) => {\n this.version++\n event.changes.keys.forEach((change, linkIdStr) => {\n this.handleLinkChange(change, linkIdStr)\n })\n })\n\n // Listen for reroute changes and update spatial indexes\n this.yreroutes.observe((event: Y.YMapEvent<Y.Map<unknown>>) => {\n this.version++\n event.changes.keys.forEach((change, rerouteIdStr) => {\n this.handleRerouteChange(change, rerouteIdStr)\n })\n })\n }\n\n private getLinkField<K extends keyof LinkData>(\n ylink: Y.Map<unknown>,\n field: K\n ): LinkData[K] | undefined {\n const typedLink = ylink as TypedYMap<LinkData>\n return typedLink.get(field)\n }\n\n private getRerouteField<K extends keyof RerouteData>(\n yreroute: Y.Map<unknown>,\n field: K,\n defaultValue: RerouteData[K] = LayoutStoreImpl.REROUTE_DEFAULTS[field]\n ): RerouteData[K] {\n const typedReroute = yreroute as TypedYMap<RerouteData>\n const value = typedReroute.get(field)\n return value ?? defaultValue\n }\n\n /**\n * Get or create a customRef for a node layout\n */\n getNodeLayoutRef(nodeId: NodeId): Ref<NodeLayout | null> {\n let nodeRef = this.nodeRefs.get(nodeId)\n\n if (!nodeRef) {\n nodeRef = customRef<NodeLayout | null>((track, trigger) => {\n // Store the trigger so we can call it when Yjs changes\n this.nodeTriggers.set(nodeId, trigger)\n\n return {\n get: () => {\n track()\n const ynode = this.ynodes.get(nodeId)\n const layout = ynode ? yNodeToLayout(ynode) : null\n return layout\n },\n set: (newLayout: NodeLayout | null) => {\n if (newLayout === null) {\n // Delete operation\n const existing = this.ynodes.get(nodeId)\n if (existing) {\n this.applyOperation({\n type: 'deleteNode',\n entity: 'node',\n nodeId,\n timestamp: Date.now(),\n source: this.currentSource,\n actor: this.currentActor,\n previousLayout: yNodeToLayout(existing)\n })\n }\n } else {\n // Update operation - detect what changed\n const existing = this.ynodes.get(nodeId)\n if (!existing) {\n // Create operation\n this.applyOperation({\n type: 'createNode',\n entity: 'node',\n nodeId,\n layout: newLayout,\n timestamp: Date.now(),\n source: this.currentSource,\n actor: this.currentActor\n })\n } else {\n const existingLayout = yNodeToLayout(existing)\n\n // Check what properties changed\n if (\n existingLayout.position.x !== newLayout.position.x ||\n existingLayout.position.y !== newLayout.position.y\n ) {\n this.applyOperation({\n type: 'moveNode',\n entity: 'node',\n nodeId,\n position: newLayout.position,\n previousPosition: existingLayout.position,\n timestamp: Date.now(),\n source: this.currentSource,\n actor: this.currentActor\n })\n }\n if (\n existingLayout.size.width !== newLayout.size.width ||\n existingLayout.size.height !== newLayout.size.height\n ) {\n this.applyOperation({\n type: 'resizeNode',\n entity: 'node',\n nodeId,\n size: newLayout.size,\n previousSize: existingLayout.size,\n timestamp: Date.now(),\n source: this.currentSource,\n actor: this.currentActor\n })\n }\n if (existingLayout.zIndex !== newLayout.zIndex) {\n this.applyOperation({\n type: 'setNodeZIndex',\n entity: 'node',\n nodeId,\n zIndex: newLayout.zIndex,\n previousZIndex: existingLayout.zIndex,\n timestamp: Date.now(),\n source: this.currentSource,\n actor: this.currentActor\n })\n }\n }\n }\n trigger()\n }\n }\n })\n\n this.nodeRefs.set(nodeId, nodeRef)\n }\n\n return nodeRef\n }\n\n /**\n * Get nodes within bounds (reactive)\n */\n getNodesInBounds(bounds: Bounds): ComputedRef<NodeId[]> {\n return computed(() => {\n // Touch version for reactivity\n void this.version\n\n const result: NodeId[] = []\n for (const [nodeId] of this.ynodes) {\n const ynode = this.ynodes.get(nodeId)\n if (ynode) {\n const layout = yNodeToLayout(ynode)\n if (layout && boundsIntersect(layout.bounds, bounds)) {\n result.push(nodeId)\n }\n }\n }\n return result\n })\n }\n\n /**\n * Get all nodes as a reactive map\n */\n getAllNodes(): ComputedRef<ReadonlyMap<NodeId, NodeLayout>> {\n return computed(() => {\n // Touch version for reactivity\n void this.version\n\n const result = new Map<NodeId, NodeLayout>()\n for (const [nodeId] of this.ynodes) {\n const ynode = this.ynodes.get(nodeId)\n if (ynode) {\n const layout = yNodeToLayout(ynode)\n if (layout) {\n result.set(nodeId, layout)\n }\n }\n }\n return result\n })\n }\n\n /**\n * Get current version for change detection\n */\n getVersion(): ComputedRef<number> {\n return computed(() => this.version)\n }\n\n /**\n * Query node at point (non-reactive for performance)\n */\n queryNodeAtPoint(point: Point): NodeId | null {\n const nodes: Array<[NodeId, NodeLayout]> = []\n\n for (const [nodeId] of this.ynodes) {\n const ynode = this.ynodes.get(nodeId)\n if (ynode) {\n const layout = yNodeToLayout(ynode)\n if (layout) {\n nodes.push([nodeId, layout])\n }\n }\n }\n\n // Sort by zIndex (top to bottom)\n nodes.sort(([, a], [, b]) => b.zIndex - a.zIndex)\n\n for (const [nodeId, layout] of nodes) {\n if (pointInBounds(point, layout.bounds)) {\n return nodeId\n }\n }\n\n return null\n }\n\n /**\n * Query nodes in bounds (non-reactive for performance)\n */\n queryNodesInBounds(bounds: Bounds): NodeId[] {\n return this.spatialIndex.query(bounds)\n }\n\n /**\n * Update link layout data (for geometry/debug, no separate spatial index)\n */\n updateLinkLayout(linkId: LinkId, layout: LinkLayout): void {\n const existing = this.linkLayouts.get(linkId)\n\n // Short-circuit if bounds and centerPos unchanged\n if (\n existing &&\n isBoundsEqual(existing.bounds, layout.bounds) &&\n isPointEqual(existing.centerPos, layout.centerPos)\n ) {\n // Only update path if provided (for hit detection)\n if (layout.path) {\n existing.path = layout.path\n }\n return\n }\n\n this.linkLayouts.set(linkId, layout)\n }\n\n /**\n * Delete link layout data\n */\n deleteLinkLayout(linkId: LinkId): void {\n const deleted = this.linkLayouts.delete(linkId)\n if (deleted) {\n // Clean up any segment layouts for this link\n const keysToDelete: string[] = []\n for (const [key] of this.linkSegmentLayouts) {\n if (key.startsWith(`${linkId}:`)) {\n keysToDelete.push(key)\n }\n }\n for (const key of keysToDelete) {\n this.linkSegmentLayouts.delete(key)\n this.linkSegmentSpatialIndex.remove(key)\n }\n }\n }\n\n /**\n * Update slot layout data\n */\n updateSlotLayout(key: string, layout: SlotLayout): void {\n const existing = this.slotLayouts.get(key)\n\n if (existing) {\n // Short-circuit if geometry is unchanged\n if (\n isPointEqual(existing.position, layout.position) &&\n isBoundsEqual(existing.bounds, layout.bounds)\n ) {\n return\n }\n // Update spatial index\n this.slotSpatialIndex.update(key, layout.bounds)\n } else {\n // Insert into spatial index\n this.slotSpatialIndex.insert(key, layout.bounds)\n }\n\n this.slotLayouts.set(key, layout)\n }\n\n /**\n * Batch update slot layouts and spatial index in one pass\n */\n batchUpdateSlotLayouts(\n updates: Array<{ key: string; layout: SlotLayout }>\n ): void {\n if (!updates.length) return\n\n // Update spatial index and map entries (skip unchanged)\n for (const { key, layout } of updates) {\n const existing = this.slotLayouts.get(key)\n\n if (existing) {\n // Short-circuit if geometry is unchanged\n if (\n isPointEqual(existing.position, layout.position) &&\n isBoundsEqual(existing.bounds, layout.bounds)\n ) {\n continue\n }\n this.slotSpatialIndex.update(key, layout.bounds)\n } else {\n this.slotSpatialIndex.insert(key, layout.bounds)\n }\n this.slotLayouts.set(key, layout)\n }\n }\n\n /**\n * Delete slot layout data\n */\n deleteSlotLayout(key: string): void {\n const deleted = this.slotLayouts.delete(key)\n if (deleted) {\n // Remove from spatial index\n this.slotSpatialIndex.remove(key)\n }\n }\n\n /**\n * Delete all slot layouts for a node\n */\n deleteNodeSlotLayouts(nodeId: NodeId): void {\n const keysToDelete: string[] = []\n for (const [key, layout] of this.slotLayouts) {\n if (layout.nodeId === nodeId) {\n keysToDelete.push(key)\n }\n }\n for (const key of keysToDelete) {\n this.slotLayouts.delete(key)\n // Remove from spatial index\n this.slotSpatialIndex.remove(key)\n }\n }\n\n /**\n * Clear all slot layouts and their spatial index (O(1) operations)\n * Used when switching rendering modes (Vue ↔ LiteGraph)\n */\n clearAllSlotLayouts(): void {\n this.slotLayouts.clear()\n this.slotSpatialIndex.clear()\n }\n\n /**\n * Update reroute layout data\n */\n updateRerouteLayout(rerouteId: RerouteId, layout: RerouteLayout): void {\n const existing = this.rerouteLayouts.get(rerouteId)\n\n if (!existing) {\n logger.debug('Adding reroute layout:', {\n rerouteId,\n position: layout.position,\n bounds: layout.bounds\n })\n }\n\n if (existing) {\n // Update spatial index\n this.rerouteSpatialIndex.update(String(rerouteId), layout.bounds) // Spatial index uses strings\n } else {\n // Insert into spatial index\n this.rerouteSpatialIndex.insert(String(rerouteId), layout.bounds) // Spatial index uses strings\n }\n\n this.rerouteLayouts.set(rerouteId, layout)\n }\n\n /**\n * Delete reroute layout data\n */\n deleteRerouteLayout(rerouteId: RerouteId): void {\n const deleted = this.rerouteLayouts.delete(rerouteId)\n if (deleted) {\n // Remove from spatial index\n this.rerouteSpatialIndex.remove(String(rerouteId)) // Spatial index uses strings\n }\n }\n\n /**\n * Get link layout data\n */\n getLinkLayout(linkId: LinkId): LinkLayout | null {\n return this.linkLayouts.get(linkId) || null\n }\n\n /**\n * Get slot layout data\n */\n getSlotLayout(key: string): SlotLayout | null {\n return this.slotLayouts.get(key) || null\n }\n\n /**\n * Get reroute layout data\n */\n getRerouteLayout(rerouteId: RerouteId): RerouteLayout | null {\n return this.rerouteLayouts.get(rerouteId) || null\n }\n\n /**\n * Returns all slot layout keys currently tracked by the store.\n * Useful for global passes without relying on spatial queries.\n */\n getAllSlotKeys(): string[] {\n return Array.from(this.slotLayouts.keys())\n }\n\n /**\n * Update link segment layout data\n */\n updateLinkSegmentLayout(\n linkId: LinkId,\n rerouteId: RerouteId | null,\n layout: Omit<LinkSegmentLayout, 'linkId' | 'rerouteId'>\n ): void {\n const key = makeLinkSegmentKey(linkId, rerouteId)\n const existing = this.linkSegmentLayouts.get(key)\n\n // Short-circuit if bounds and centerPos unchanged (prevents spatial index churn)\n if (\n existing &&\n isBoundsEqual(existing.bounds, layout.bounds) &&\n isPointEqual(existing.centerPos, layout.centerPos)\n ) {\n // Only update path if provided (for hit detection)\n if (layout.path) {\n existing.path = layout.path\n }\n return\n }\n\n const fullLayout: LinkSegmentLayout = {\n ...layout,\n linkId,\n rerouteId\n }\n\n if (!existing) {\n logger.debug('Adding link segment:', {\n linkId,\n rerouteId,\n bounds: layout.bounds,\n hasPath: !!layout.path\n })\n }\n\n if (existing) {\n // Update spatial index\n this.linkSegmentSpatialIndex.update(key, layout.bounds)\n } else {\n // Insert into spatial index\n this.linkSegmentSpatialIndex.insert(key, layout.bounds)\n }\n\n this.linkSegmentLayouts.set(key, fullLayout)\n }\n\n /**\n * Delete link segment layout data\n */\n deleteLinkSegmentLayout(linkId: LinkId, rerouteId: RerouteId | null): void {\n const key = makeLinkSegmentKey(linkId, rerouteId)\n const deleted = this.linkSegmentLayouts.delete(key)\n if (deleted) {\n // Remove from spatial index\n this.linkSegmentSpatialIndex.remove(key)\n }\n }\n\n /**\n * Query link segment at point (returns structured data)\n */\n queryLinkSegmentAtPoint(\n point: Point,\n ctx?: CanvasRenderingContext2D\n ): { linkId: LinkId; rerouteId: RerouteId | null } | null {\n // Determine tolerance from current canvas state (if available)\n // - Use the caller-provided ctx.lineWidth (LGraphCanvas sets this to connections_width + padding)\n // - Fall back to a sensible default when ctx is not provided\n const hitWidth = ctx?.lineWidth ?? 10\n const halfSize = Math.max(10, hitWidth) // keep a minimum window for spatial index\n\n // Use spatial index to get candidate segments\n const searchArea = {\n x: point.x - halfSize,\n y: point.y - halfSize,\n width: halfSize * 2,\n height: halfSize * 2\n }\n const candidateKeys = this.linkSegmentSpatialIndex.query(searchArea)\n\n if (candidateKeys.length > 0) {\n logger.debug('Checking link segments at point:', {\n point,\n candidateCount: candidateKeys.length,\n tolerance: hitWidth\n })\n }\n\n // Precise hit test only on candidates\n for (const key of candidateKeys) {\n const segmentLayout = this.linkSegmentLayouts.get(key)\n if (!segmentLayout) continue\n\n if (ctx && segmentLayout.path) {\n // Match LiteGraph behavior: hit test uses device pixel ratio for coordinates\n const dpi =\n (typeof window !== 'undefined' && window?.devicePixelRatio) || 1\n const hit = ctx.isPointInStroke(\n segmentLayout.path,\n point.x * dpi,\n point.y * dpi\n )\n\n if (hit) {\n logger.debug('Link segment hit:', {\n linkId: segmentLayout.linkId,\n rerouteId: segmentLayout.rerouteId,\n point\n })\n return {\n linkId: segmentLayout.linkId,\n rerouteId: segmentLayout.rerouteId\n }\n }\n } else if (pointInBounds(point, segmentLayout.bounds)) {\n // Fallback to bounding box test\n return {\n linkId: segmentLayout.linkId,\n rerouteId: segmentLayout.rerouteId\n }\n }\n }\n\n return null\n }\n\n /**\n * Query link at point (derived from segment query)\n */\n queryLinkAtPoint(\n point: Point,\n ctx?: CanvasRenderingContext2D\n ): LinkId | null {\n // Invoke segment query and return just the linkId\n const segment = this.queryLinkSegmentAtPoint(point, ctx)\n return segment ? segment.linkId : null\n }\n\n /**\n * Query slot at point\n */\n querySlotAtPoint(point: Point): SlotLayout | null {\n // Use spatial index to get candidate slots\n const searchArea = {\n x: point.x - 10, // Tolerance for slot size\n y: point.y - 10,\n width: 20,\n height: 20\n }\n const candidateSlotKeys = this.slotSpatialIndex.query(searchArea)\n\n // Check precise bounds for candidates\n for (const key of candidateSlotKeys) {\n const slotLayout = this.slotLayouts.get(key)\n if (slotLayout && pointInBounds(point, slotLayout.bounds)) {\n return slotLayout\n }\n }\n return null\n }\n\n /**\n * Query reroute at point\n */\n queryRerouteAtPoint(point: Point): RerouteLayout | null {\n // Use spatial index to get candidate reroutes\n const maxRadius = 20 // Maximum expected reroute radius\n const searchArea = {\n x: point.x - maxRadius,\n y: point.y - maxRadius,\n width: maxRadius * 2,\n height: maxRadius * 2\n }\n const candidateRerouteKeys = this.rerouteSpatialIndex.query(searchArea)\n\n if (candidateRerouteKeys.length > 0) {\n logger.debug('Checking reroutes at point:', {\n point,\n candidateCount: candidateRerouteKeys.length\n })\n }\n\n // Check precise distance for candidates\n for (const rerouteKey of candidateRerouteKeys) {\n const rerouteId = asRerouteId(rerouteKey)\n const rerouteLayout = this.rerouteLayouts.get(rerouteId)\n if (rerouteLayout) {\n const dx = point.x - rerouteLayout.position.x\n const dy = point.y - rerouteLayout.position.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n if (distance <= rerouteLayout.radius) {\n logger.debug('Reroute hit:', {\n rerouteId: rerouteLayout.id,\n position: rerouteLayout.position,\n distance\n })\n return rerouteLayout\n }\n }\n }\n return null\n }\n\n /**\n * Query all items in bounds\n */\n queryItemsInBounds(bounds: Bounds): {\n nodes: NodeId[]\n links: LinkId[]\n slots: string[]\n reroutes: RerouteId[]\n } {\n // Query segments and union their linkIds\n const segmentKeys = this.linkSegmentSpatialIndex.query(bounds)\n const linkIds = new Set<LinkId>()\n for (const key of segmentKeys) {\n const segment = this.linkSegmentLayouts.get(key)\n if (segment) {\n linkIds.add(segment.linkId)\n }\n }\n\n return {\n nodes: this.queryNodesInBounds(bounds),\n links: Array.from(linkIds),\n slots: this.slotSpatialIndex.query(bounds),\n reroutes: this.rerouteSpatialIndex\n .query(bounds)\n .map((key) => asRerouteId(key))\n }\n }\n\n /**\n * Apply a layout operation using Yjs transactions\n */\n applyOperation(operation: LayoutOperation): void {\n // Create change object outside transaction so we can use it after\n const change: LayoutChange = {\n type: 'update',\n nodeIds: [],\n timestamp: operation.timestamp,\n source: operation.source,\n operation\n }\n\n // Use Yjs transaction for atomic updates\n this.ydoc.transact(() => {\n // Add operation to log\n this.yoperations.push([operation])\n\n // Apply the operation\n this.applyOperationInTransaction(operation, change)\n }, this.currentActor)\n\n // Post-transaction updates\n this.finalizeOperation(change)\n }\n\n /**\n * Apply operation within a transaction\n */\n private applyOperationInTransaction(\n operation: LayoutOperation,\n change: LayoutChange\n ): void {\n switch (operation.type) {\n case 'moveNode':\n this.handleMoveNode(operation as MoveNodeOperation, change)\n break\n case 'resizeNode':\n this.handleResizeNode(operation as ResizeNodeOperation, change)\n break\n case 'setNodeZIndex':\n this.handleSetNodeZIndex(operation as SetNodeZIndexOperation, change)\n break\n case 'createNode':\n this.handleCreateNode(operation as CreateNodeOperation, change)\n break\n case 'deleteNode':\n this.handleDeleteNode(operation as DeleteNodeOperation, change)\n break\n case 'batchUpdateBounds':\n this.handleBatchUpdateBounds(\n operation as BatchUpdateBoundsOperation,\n change\n )\n break\n case 'createLink':\n this.handleCreateLink(operation as CreateLinkOperation, change)\n break\n case 'deleteLink':\n this.handleDeleteLink(operation as DeleteLinkOperation, change)\n break\n case 'createReroute':\n this.handleCreateReroute(operation as CreateRerouteOperation, change)\n break\n case 'deleteReroute':\n this.handleDeleteReroute(operation as DeleteRerouteOperation, change)\n break\n case 'moveReroute':\n this.handleMoveReroute(operation as MoveRerouteOperation, change)\n break\n }\n }\n\n /**\n * Finalize operation after transaction\n */\n private finalizeOperation(change: LayoutChange): void {\n // Update version\n this.version++\n\n // Manually trigger affected node refs after transaction\n // This is needed because Yjs observers don't fire for property changes\n change.nodeIds.forEach((nodeId) => {\n const trigger = this.nodeTriggers.get(nodeId)\n if (trigger) {\n trigger()\n }\n })\n\n // Notify listeners (after transaction completes)\n setTimeout(() => this.notifyChange(change), 0)\n }\n\n /**\n * Subscribe to layout changes\n */\n onChange(callback: (change: LayoutChange) => void): () => void {\n this.changeListeners.add(callback)\n return () => this.changeListeners.delete(callback)\n }\n\n /**\n * Set the current operation source\n */\n setSource(source: LayoutSource): void {\n this.currentSource = source\n }\n\n /**\n * Set the current actor (for CRDT)\n */\n setActor(actor: string): void {\n this.currentActor = actor\n }\n\n /**\n * Get the current operation source\n */\n getCurrentSource(): LayoutSource {\n return this.currentSource\n }\n\n /**\n * Get the current actor\n */\n getCurrentActor(): string {\n return this.currentActor\n }\n\n /**\n * Clean up refs and triggers for a node when its Vue component unmounts.\n * This should be called from the component's onUnmounted hook.\n */\n cleanupNodeRef(nodeId: NodeId): void {\n this.nodeRefs.delete(nodeId)\n this.nodeTriggers.delete(nodeId)\n }\n\n /**\n * Initialize store with existing nodes\n */\n initializeFromLiteGraph(\n nodes: Array<{ id: string; pos: [number, number]; size: [number, number] }>\n ): void {\n this.ydoc.transact(() => {\n this.ynodes.clear()\n // Note: We intentionally do NOT clear nodeRefs and nodeTriggers here.\n // Vue components may already hold references to these refs, and clearing\n // them would break the reactivity chain. The refs will be reused when\n // nodes are recreated, and stale refs will be cleaned up over time.\n this.spatialIndex.clear()\n this.linkSegmentSpatialIndex.clear()\n this.slotSpatialIndex.clear()\n this.rerouteSpatialIndex.clear()\n this.linkLayouts.clear()\n this.linkSegmentLayouts.clear()\n this.slotLayouts.clear()\n this.rerouteLayouts.clear()\n\n nodes.forEach((node, index) => {\n const layout: NodeLayout = {\n id: node.id.toString(),\n position: { x: node.pos[0], y: node.pos[1] },\n size: { width: node.size[0], height: node.size[1] },\n zIndex: index,\n visible: true,\n bounds: {\n x: node.pos[0],\n y: node.pos[1],\n width: node.size[0],\n height: node.size[1]\n }\n }\n\n this.ynodes.set(layout.id, layoutToYNode(layout))\n\n // Add to spatial index\n this.spatialIndex.insert(layout.id, layout.bounds)\n })\n\n // Trigger all existing refs to notify Vue of the new data\n this.nodeTriggers.forEach((trigger) => trigger())\n }, 'initialization')\n }\n\n // Operation handlers\n private handleMoveNode(\n operation: MoveNodeOperation,\n change: LayoutChange\n ): void {\n const ynode = this.ynodes.get(operation.nodeId)\n if (!ynode) {\n return\n }\n\n const size = yNodeToLayout(ynode).size\n const newBounds = {\n x: operation.position.x,\n y: operation.position.y,\n width: size.width,\n height: size.height\n }\n\n // Update spatial index FIRST, synchronously to prevent race conditions\n // Hit detection queries can run before CRDT updates complete\n this.spatialIndex.update(operation.nodeId, newBounds)\n\n // Then update CRDT\n ynode.set('position', operation.position)\n this.updateNodeBounds(ynode, operation.position, size)\n\n change.nodeIds.push(operation.nodeId)\n }\n\n private handleResizeNode(\n operation: ResizeNodeOperation,\n change: LayoutChange\n ): void {\n const ynode = this.ynodes.get(operation.nodeId)\n if (!ynode) return\n\n const position = yNodeToLayout(ynode).position\n const newBounds = {\n x: position.x,\n y: position.y,\n width: operation.size.width,\n height: operation.size.height\n }\n\n // Update spatial index FIRST, synchronously to prevent race conditions\n // Hit detection queries can run before CRDT updates complete\n this.spatialIndex.update(operation.nodeId, newBounds)\n\n // Then update CRDT\n ynode.set('size', operation.size)\n this.updateNodeBounds(ynode, position, operation.size)\n\n change.nodeIds.push(operation.nodeId)\n }\n\n private handleSetNodeZIndex(\n operation: SetNodeZIndexOperation,\n change: LayoutChange\n ): void {\n const ynode = this.ynodes.get(operation.nodeId)\n if (!ynode) return\n\n ynode.set('zIndex', operation.zIndex)\n change.nodeIds.push(operation.nodeId)\n }\n\n private handleCreateNode(\n operation: CreateNodeOperation,\n change: LayoutChange\n ): void {\n const ynode = layoutToYNode(operation.layout)\n this.ynodes.set(operation.nodeId, ynode)\n\n // Add to spatial index\n this.spatialIndex.insert(operation.nodeId, operation.layout.bounds)\n\n change.type = 'create'\n change.nodeIds.push(operation.nodeId)\n }\n\n private handleDeleteNode(\n operation: DeleteNodeOperation,\n change: LayoutChange\n ): void {\n if (!this.ynodes.has(operation.nodeId)) return\n\n this.ynodes.delete(operation.nodeId)\n // Note: We intentionally do NOT delete nodeRefs and nodeTriggers here.\n // During undo/redo, Vue components may still hold references to the old ref.\n // If we delete the trigger, Vue won't be notified when the node is re-created.\n // The trigger will be called in finalizeOperation to notify Vue of the change.\n\n // Remove from spatial index\n this.spatialIndex.remove(operation.nodeId)\n\n // Clean up associated slot layouts\n this.deleteNodeSlotLayouts(operation.nodeId)\n\n // Clean up associated links\n const linksToDelete = this.findLinksConnectedToNode(operation.nodeId)\n\n // Delete the associated links\n for (const linkId of linksToDelete) {\n this.ylinks.delete(String(linkId))\n this.linkLayouts.delete(linkId)\n\n // Clean up link segment layouts\n this.cleanupLinkSegments(linkId)\n }\n\n change.type = 'delete'\n change.nodeIds.push(operation.nodeId)\n }\n\n private handleBatchUpdateBounds(\n operation: BatchUpdateBoundsOperation,\n change: LayoutChange\n ): void {\n const spatialUpdates: Array<{ nodeId: NodeId; bounds: Bounds }> = []\n\n for (const nodeId of operation.nodeIds) {\n const data = operation.bounds[nodeId]\n const ynode = this.ynodes.get(nodeId)\n if (!ynode || !data) continue\n\n ynode.set('position', { x: data.bounds.x, y: data.bounds.y })\n ynode.set('size', {\n width: data.bounds.width,\n height: data.bounds.height\n })\n ynode.set('bounds', data.bounds)\n\n spatialUpdates.push({ nodeId, bounds: data.bounds })\n change.nodeIds.push(nodeId)\n }\n\n // Batch update spatial index for better performance\n if (spatialUpdates.length > 0) {\n this.spatialIndex.batchUpdate(spatialUpdates)\n }\n\n if (change.nodeIds.length) {\n change.type = 'update'\n }\n }\n\n private handleCreateLink(\n operation: CreateLinkOperation,\n change: LayoutChange\n ): void {\n const linkData = new Y.Map<unknown>()\n linkData.set('id', operation.linkId)\n linkData.set('sourceNodeId', operation.sourceNodeId)\n linkData.set('sourceSlot', operation.sourceSlot)\n linkData.set('targetNodeId', operation.targetNodeId)\n linkData.set('targetSlot', operation.targetSlot)\n\n this.ylinks.set(String(operation.linkId), linkData)\n\n // Link geometry will be computed separately when nodes move\n // This just tracks that the link exists\n change.type = 'create'\n }\n\n private handleDeleteLink(\n operation: DeleteLinkOperation,\n change: LayoutChange\n ): void {\n if (!this.ylinks.has(String(operation.linkId))) return\n\n this.ylinks.delete(String(operation.linkId))\n this.linkLayouts.delete(operation.linkId)\n // Clean up any segment layouts for this link\n this.cleanupLinkSegments(operation.linkId)\n\n change.type = 'delete'\n }\n\n private handleCreateReroute(\n operation: CreateRerouteOperation,\n change: LayoutChange\n ): void {\n const rerouteData = new Y.Map<unknown>()\n rerouteData.set('id', operation.rerouteId)\n rerouteData.set('position', operation.position)\n rerouteData.set('parentId', operation.parentId)\n rerouteData.set('linkIds', operation.linkIds)\n\n this.yreroutes.set(String(operation.rerouteId), rerouteData) // Yjs Map keys must be strings\n\n // The observer will automatically update the spatial index\n change.type = 'create'\n }\n\n private handleDeleteReroute(\n operation: DeleteRerouteOperation,\n change: LayoutChange\n ): void {\n if (!this.yreroutes.has(String(operation.rerouteId))) return // Yjs Map keys are strings\n\n this.yreroutes.delete(String(operation.rerouteId)) // Yjs Map keys are strings\n this.rerouteLayouts.delete(operation.rerouteId) // Layout map uses numeric ID\n this.rerouteSpatialIndex.remove(String(operation.rerouteId)) // Spatial index uses strings\n\n change.type = 'delete'\n }\n\n private handleMoveReroute(\n operation: MoveRerouteOperation,\n change: LayoutChange\n ): void {\n const yreroute = this.yreroutes.get(String(operation.rerouteId)) // Yjs Map keys are strings\n if (!yreroute) return\n\n yreroute.set('position', operation.position)\n\n const pos = operation.position\n const layout: RerouteLayout = {\n id: operation.rerouteId,\n position: pos,\n radius: 8,\n bounds: {\n x: pos.x - 8,\n y: pos.y - 8,\n width: 16,\n height: 16\n }\n }\n this.updateRerouteLayout(operation.rerouteId, layout)\n\n // Mark as update for listeners\n change.type = 'update'\n }\n\n /**\n * Update node bounds helper\n */\n private updateNodeBounds(\n ynode: NodeLayoutMap,\n position: Point,\n size: { width: number; height: number }\n ): void {\n ynode.set('bounds', {\n x: position.x,\n y: position.y,\n width: size.width,\n height: size.height\n })\n }\n\n /**\n * Find all links connected to a specific node\n */\n private findLinksConnectedToNode(nodeId: NodeId): LinkId[] {\n const connectedLinks: LinkId[] = []\n this.ylinks.forEach((linkData: Y.Map<unknown>, linkIdStr: string) => {\n const linkId = asLinkId(linkIdStr)\n const sourceNodeId = this.getLinkField(linkData, 'sourceNodeId')\n const targetNodeId = this.getLinkField(linkData, 'targetNodeId')\n\n if (sourceNodeId === nodeId || targetNodeId === nodeId) {\n connectedLinks.push(linkId)\n }\n })\n return connectedLinks\n }\n\n /**\n * Handle link change events\n */\n private handleLinkChange(change: YEventChange, linkIdStr: string): void {\n if (change.action === 'delete') {\n const linkId = asLinkId(linkIdStr)\n this.cleanupLinkData(linkId)\n }\n // Link was added or updated - geometry will be computed separately\n // This just tracks that the link exists in CRDT\n }\n\n /**\n * Clean up all data associated with a link\n */\n private cleanupLinkData(linkId: LinkId): void {\n this.linkLayouts.delete(linkId)\n this.cleanupLinkSegments(linkId)\n }\n\n /**\n * Clean up all segment layouts for a link\n */\n private cleanupLinkSegments(linkId: LinkId): void {\n const keysToDelete: string[] = []\n for (const [key] of this.linkSegmentLayouts) {\n if (key.startsWith(`${linkId}:`)) {\n keysToDelete.push(key)\n }\n }\n\n for (const key of keysToDelete) {\n this.linkSegmentLayouts.delete(key)\n this.linkSegmentSpatialIndex.remove(key)\n }\n }\n\n /**\n * Handle reroute change events\n */\n private handleRerouteChange(\n change: YEventChange,\n rerouteIdStr: string\n ): void {\n const rerouteId = asRerouteId(rerouteIdStr)\n\n if (change.action === 'delete') {\n this.handleRerouteDelete(rerouteId)\n } else {\n this.handleRerouteUpsert(rerouteId)\n }\n }\n\n /**\n * Handle reroute deletion\n */\n private handleRerouteDelete(rerouteId: RerouteId): void {\n this.rerouteLayouts.delete(rerouteId)\n this.rerouteSpatialIndex.remove(String(rerouteId))\n }\n\n /**\n * Handle reroute upsert (update if exists, create if not)\n */\n private handleRerouteUpsert(rerouteId: RerouteId): void {\n const rerouteData = this.yreroutes.get(String(rerouteId))\n if (!rerouteData) return\n\n const position = this.getRerouteField(rerouteData, 'position')\n if (!position) return\n\n const layout = this.createRerouteLayout(rerouteId, position)\n this.updateRerouteLayout(rerouteId, layout)\n }\n\n /**\n * Create reroute layout from position\n */\n private createRerouteLayout(\n rerouteId: RerouteId,\n position: Point\n ): RerouteLayout {\n return {\n id: rerouteId,\n position,\n radius: REROUTE_RADIUS,\n bounds: {\n x: position.x - REROUTE_RADIUS,\n y: position.y - REROUTE_RADIUS,\n width: REROUTE_RADIUS * 2,\n height: REROUTE_RADIUS * 2\n }\n }\n }\n\n // Helper methods\n\n private notifyChange(change: LayoutChange): void {\n this.changeListeners.forEach((listener) => {\n try {\n listener(change)\n } catch (error) {\n console.error('Error in layout change listener:', error)\n }\n })\n }\n\n // CRDT-specific methods\n getOperationsSince(timestamp: number): LayoutOperation[] {\n const operations: LayoutOperation[] = []\n this.yoperations.forEach((op: LayoutOperation) => {\n if (op && op.timestamp > timestamp) {\n operations.push(op)\n }\n })\n return operations\n }\n\n getOperationsByActor(actor: string): LayoutOperation[] {\n const operations: LayoutOperation[] = []\n this.yoperations.forEach((op: LayoutOperation) => {\n if (op && op.actor === actor) {\n operations.push(op)\n }\n })\n return operations\n }\n\n /**\n * Get the Yjs document for network sync (future feature)\n */\n getYDoc(): Y.Doc {\n return this.ydoc\n }\n\n /**\n * Apply updates from remote peers (future feature)\n */\n applyUpdate(update: Uint8Array): void {\n Y.applyUpdate(this.ydoc, update)\n }\n\n /**\n * Get state as update for sending to peers (future feature)\n */\n getStateAsUpdate(): Uint8Array {\n return Y.encodeStateAsUpdate(this.ydoc)\n }\n\n /**\n * Batch update node bounds using Yjs transaction for atomicity.\n */\n batchUpdateNodeBounds(updates: NodeBoundsUpdate[]): void {\n if (updates.length === 0) return\n\n const originalSource = this.currentSource\n const shouldNormalizeHeights = originalSource === LayoutSource.DOM\n this.currentSource = LayoutSource.Vue\n\n const nodeIds: NodeId[] = []\n const boundsRecord: BatchUpdateBoundsOperation['bounds'] = {}\n\n for (const { nodeId, bounds } of updates) {\n const ynode = this.ynodes.get(nodeId)\n if (!ynode) continue\n const currentLayout = yNodeToLayout(ynode)\n\n const normalizedBounds = shouldNormalizeHeights\n ? {\n ...bounds,\n height: removeNodeTitleHeight(bounds.height)\n }\n : bounds\n\n boundsRecord[nodeId] = {\n bounds: normalizedBounds,\n previousBounds: currentLayout.bounds\n }\n nodeIds.push(nodeId)\n }\n\n if (!nodeIds.length) {\n this.currentSource = originalSource\n return\n }\n\n const operation: BatchUpdateBoundsOperation = {\n type: 'batchUpdateBounds',\n entity: 'node',\n nodeIds,\n bounds: boundsRecord,\n timestamp: Date.now(),\n source: this.currentSource,\n actor: this.currentActor\n }\n\n this.applyOperation(operation)\n\n this.currentSource = originalSource\n }\n}\n\n// Create singleton instance\nexport const layoutStore = new LayoutStoreImpl()\n","/**\n * Layout Mutations - Simplified Direct Operations\n *\n * Provides a clean API for layout operations that are CRDT-ready.\n * Operations are synchronous and applied directly to the store.\n */\nimport log from 'loglevel'\n\nimport type { NodeId } from '@/lib/litegraph/src/LGraphNode'\nimport { layoutStore } from '@/renderer/core/layout/store/layoutStore'\nimport type {\n LayoutSource,\n LinkId,\n NodeLayout,\n Point,\n RerouteId,\n Size\n} from '@/renderer/core/layout/types'\n\nconst logger = log.getLogger('LayoutMutations')\n\ninterface LayoutMutations {\n // Single node operations (synchronous, CRDT-ready)\n moveNode(nodeId: NodeId, position: Point): void\n resizeNode(nodeId: NodeId, size: Size): void\n setNodeZIndex(nodeId: NodeId, zIndex: number): void\n\n // Node lifecycle operations\n createNode(nodeId: NodeId, layout: Partial<NodeLayout>): void\n deleteNode(nodeId: NodeId): void\n\n // Link operations\n createLink(\n linkId: LinkId,\n sourceNodeId: NodeId,\n sourceSlot: number,\n targetNodeId: NodeId,\n targetSlot: number\n ): void\n deleteLink(linkId: LinkId): void\n\n // Reroute operations\n createReroute(\n rerouteId: RerouteId,\n position: Point,\n parentId?: LinkId,\n linkIds?: LinkId[]\n ): void\n deleteReroute(rerouteId: RerouteId): void\n moveReroute(\n rerouteId: RerouteId,\n position: Point,\n previousPosition: Point\n ): void\n\n // Stacking operations\n bringNodeToFront(nodeId: NodeId): void\n\n // Source tracking\n setSource(source: LayoutSource): void\n setActor(actor: string): void\n}\n\n/**\n * Composable for accessing layout mutations with clean destructuring API\n */\nexport function useLayoutMutations(): LayoutMutations {\n /**\n * Set the current mutation source\n */\n const setSource = (source: LayoutSource): void => {\n layoutStore.setSource(source)\n }\n\n /**\n * Set the current actor (for CRDT)\n */\n const setActor = (actor: string): void => {\n layoutStore.setActor(actor)\n }\n\n /**\n * Move a node to a new position\n */\n const moveNode = (nodeId: NodeId, position: Point): void => {\n const normalizedNodeId = String(nodeId)\n const existing = layoutStore.getNodeLayoutRef(normalizedNodeId).value\n if (!existing) return\n\n layoutStore.applyOperation({\n type: 'moveNode',\n entity: 'node',\n nodeId: normalizedNodeId,\n position,\n previousPosition: existing.position,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Resize a node\n */\n const resizeNode = (nodeId: NodeId, size: Size): void => {\n const normalizedNodeId = String(nodeId)\n const existing = layoutStore.getNodeLayoutRef(normalizedNodeId).value\n if (!existing) return\n\n layoutStore.applyOperation({\n type: 'resizeNode',\n entity: 'node',\n nodeId: normalizedNodeId,\n size,\n previousSize: existing.size,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Set node z-index\n */\n const setNodeZIndex = (nodeId: NodeId, zIndex: number): void => {\n const normalizedNodeId = String(nodeId)\n const existing = layoutStore.getNodeLayoutRef(normalizedNodeId).value\n if (!existing) return\n\n layoutStore.applyOperation({\n type: 'setNodeZIndex',\n entity: 'node',\n nodeId: normalizedNodeId,\n zIndex,\n previousZIndex: existing.zIndex,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Create a new node\n */\n const createNode = (nodeId: NodeId, layout: Partial<NodeLayout>): void => {\n const normalizedNodeId = String(nodeId)\n const fullLayout: NodeLayout = {\n id: normalizedNodeId,\n position: layout.position ?? { x: 0, y: 0 },\n size: layout.size ?? { width: 200, height: 100 },\n zIndex: layout.zIndex ?? 0,\n visible: layout.visible ?? true,\n bounds: {\n x: layout.position?.x ?? 0,\n y: layout.position?.y ?? 0,\n width: layout.size?.width ?? 200,\n height: layout.size?.height ?? 100\n }\n }\n\n layoutStore.applyOperation({\n type: 'createNode',\n entity: 'node',\n nodeId: normalizedNodeId,\n layout: fullLayout,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Delete a node\n */\n const deleteNode = (nodeId: NodeId): void => {\n const normalizedNodeId = String(nodeId)\n const existing = layoutStore.getNodeLayoutRef(normalizedNodeId).value\n if (!existing) return\n\n layoutStore.applyOperation({\n type: 'deleteNode',\n entity: 'node',\n nodeId: normalizedNodeId,\n previousLayout: existing,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Bring a node to the front (highest z-index)\n */\n const bringNodeToFront = (nodeId: NodeId): void => {\n // Get all nodes to find the highest z-index\n const allNodes = layoutStore.getAllNodes().value\n let maxZIndex = 0\n\n for (const [, layout] of allNodes) {\n if (layout.zIndex > maxZIndex) {\n maxZIndex = layout.zIndex\n }\n }\n\n // Set this node's z-index to be one higher than the current max\n setNodeZIndex(nodeId, maxZIndex + 1)\n }\n\n /**\n * Create a new link\n */\n const createLink = (\n linkId: LinkId,\n sourceNodeId: NodeId,\n sourceSlot: number,\n targetNodeId: NodeId,\n targetSlot: number\n ): void => {\n // Normalize node IDs to strings for layout store consistency\n const normalizedSourceNodeId = String(sourceNodeId)\n const normalizedTargetNodeId = String(targetNodeId)\n\n logger.debug('Creating link:', {\n linkId,\n from: `${normalizedSourceNodeId}[${sourceSlot}]`,\n to: `${normalizedTargetNodeId}[${targetSlot}]`\n })\n layoutStore.applyOperation({\n type: 'createLink',\n entity: 'link',\n linkId,\n sourceNodeId: normalizedSourceNodeId,\n sourceSlot,\n targetNodeId: normalizedTargetNodeId,\n targetSlot,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Delete a link\n */\n const deleteLink = (linkId: LinkId): void => {\n logger.debug('Deleting link:', linkId)\n layoutStore.applyOperation({\n type: 'deleteLink',\n entity: 'link',\n linkId,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Create a new reroute\n */\n const createReroute = (\n rerouteId: RerouteId,\n position: Point,\n parentId?: LinkId,\n linkIds: LinkId[] = []\n ): void => {\n logger.debug('Creating reroute:', {\n rerouteId,\n position,\n parentId,\n linkCount: linkIds.length\n })\n layoutStore.applyOperation({\n type: 'createReroute',\n entity: 'reroute',\n rerouteId,\n position,\n parentId,\n linkIds,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Delete a reroute\n */\n const deleteReroute = (rerouteId: RerouteId): void => {\n logger.debug('Deleting reroute:', rerouteId)\n layoutStore.applyOperation({\n type: 'deleteReroute',\n entity: 'reroute',\n rerouteId,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n /**\n * Move a reroute\n */\n const moveReroute = (\n rerouteId: RerouteId,\n position: Point,\n previousPosition: Point\n ): void => {\n logger.debug('Moving reroute:', {\n rerouteId,\n from: previousPosition,\n to: position\n })\n layoutStore.applyOperation({\n type: 'moveReroute',\n entity: 'reroute',\n rerouteId,\n position,\n previousPosition,\n timestamp: Date.now(),\n source: layoutStore.getCurrentSource(),\n actor: layoutStore.getCurrentActor()\n })\n }\n\n return {\n setSource,\n setActor,\n moveNode,\n resizeNode,\n setNodeZIndex,\n createNode,\n deleteNode,\n bringNodeToFront,\n createLink,\n deleteLink,\n createReroute,\n deleteReroute,\n moveReroute\n }\n}\n","/**\n * Constants for group node type prefixes and separators\n *\n * v1 Prefix + Separator: workflow/\n * v2 Prefix + Separator: workflow> (ComfyUI_frontend v1.2.63)\n */\nexport const PREFIX = 'workflow'\nexport const SEPARATOR = '>'\n","/**\n * Path Renderer\n *\n * Pure canvas2D rendering utility with no framework dependencies.\n * Renders bezier curves, straight lines, and linear connections between points.\n * Supports arrows, flow animations, and returns Path2D objects for hit detection.\n * Can be reused in any canvas-based project without modification.\n */\n\nexport interface Point {\n x: number\n y: number\n}\n\nexport type Direction = 'left' | 'right' | 'up' | 'down' | 'none'\nexport type RenderMode = 'spline' | 'straight' | 'linear'\nexport type ArrowShape = 'triangle' | 'circle' | 'square'\n\nexport interface LinkRenderData {\n id: string\n startPoint: Point\n endPoint: Point\n startDirection: Direction\n endDirection: Direction\n color?: string\n type?: string\n controlPoints?: Point[]\n flow?: boolean\n disabled?: boolean\n // Optional multi-segment support\n segments?: Array<{\n start: Point\n end: Point\n controlPoints?: Point[]\n }>\n // Center point storage (for hit detection and menu)\n centerPos?: Point\n centerAngle?: number\n}\n\ninterface RenderStyle {\n mode: RenderMode\n connectionWidth: number\n borderWidth?: number\n arrowShape?: ArrowShape\n showArrows?: boolean\n lowQuality?: boolean\n // Center marker properties\n showCenterMarker?: boolean\n centerMarkerShape?: 'circle' | 'arrow'\n highQuality?: boolean\n}\n\ninterface RenderColors {\n default: string\n byType: Record<string, string>\n highlighted: string\n}\n\nexport interface RenderContext {\n style: RenderStyle\n colors: RenderColors\n patterns?: {\n disabled?: CanvasPattern | null\n }\n animation?: {\n time: number // Seconds for flow animation\n }\n scale?: number // Canvas scale for quality adjustments\n highlightedIds?: Set<string>\n}\n\ninterface DragLinkData {\n /** Fixed end - the slot being dragged from */\n fixedPoint: Point\n fixedDirection: Direction\n /** Moving end - follows mouse */\n dragPoint: Point\n dragDirection?: Direction\n /** Visual properties */\n color?: string\n type?: string\n disabled?: boolean\n /** Whether dragging from input (reverse direction) */\n fromInput?: boolean\n}\n\nexport class CanvasPathRenderer {\n /**\n * Draw a link between two points\n * Returns a Path2D object for hit detection\n */\n drawLink(\n ctx: CanvasRenderingContext2D,\n link: LinkRenderData,\n context: RenderContext\n ): Path2D {\n const path = new Path2D()\n\n // Determine final color\n const isHighlighted = context.highlightedIds?.has(link.id) ?? false\n const color = this.determineLinkColor(link, context, isHighlighted)\n\n // Save context state\n ctx.save()\n\n // Apply disabled pattern if needed\n if (link.disabled && context.patterns?.disabled) {\n ctx.strokeStyle = context.patterns.disabled\n } else {\n ctx.strokeStyle = color\n }\n\n // Set line properties\n ctx.lineWidth = context.style.connectionWidth\n ctx.lineJoin = 'round'\n\n // Draw border if needed\n if (context.style.borderWidth && !context.style.lowQuality) {\n this.drawLinkPath(\n ctx,\n path,\n link,\n context,\n context.style.connectionWidth + context.style.borderWidth,\n 'rgba(0,0,0,0.5)'\n )\n }\n\n // Draw main link\n this.drawLinkPath(\n ctx,\n path,\n link,\n context,\n context.style.connectionWidth,\n color\n )\n\n // Calculate and store center position\n this.calculateCenterPoint(link, context)\n\n // Draw arrows if needed\n if (context.style.showArrows) {\n this.drawArrows(ctx, link, context, color)\n }\n\n // Draw center marker if needed (for link menu interaction)\n if (\n context.style.showCenterMarker &&\n context.scale &&\n context.scale >= 0.6 &&\n context.style.highQuality\n ) {\n this.drawCenterMarker(ctx, link, context, color)\n }\n\n // Draw flow animation if needed\n if (link.flow && context.animation) {\n this.drawFlowAnimation(ctx, path, link, context)\n }\n\n ctx.restore()\n\n return path\n }\n\n private determineLinkColor(\n link: LinkRenderData,\n context: RenderContext,\n isHighlighted: boolean\n ): string {\n if (isHighlighted) {\n return context.colors.highlighted\n }\n if (link.color) {\n return link.color\n }\n if (link.type && context.colors.byType[link.type]) {\n return context.colors.byType[link.type]\n }\n return context.colors.default\n }\n\n private drawLinkPath(\n ctx: CanvasRenderingContext2D,\n path: Path2D,\n link: LinkRenderData,\n context: RenderContext,\n lineWidth: number,\n color: string\n ): void {\n ctx.strokeStyle = color\n ctx.lineWidth = lineWidth\n\n const start = link.startPoint\n const end = link.endPoint\n\n // Build the path based on render mode\n if (context.style.mode === 'linear') {\n this.buildLinearPath(\n path,\n start,\n end,\n link.startDirection,\n link.endDirection\n )\n } else if (context.style.mode === 'straight') {\n this.buildStraightPath(\n path,\n start,\n end,\n link.startDirection,\n link.endDirection\n )\n } else {\n // Spline mode (default)\n this.buildSplinePath(\n path,\n start,\n end,\n link.startDirection,\n link.endDirection,\n link.controlPoints\n )\n }\n\n ctx.stroke(path)\n }\n\n private buildLinearPath(\n path: Path2D,\n start: Point,\n end: Point,\n startDir: Direction,\n endDir: Direction\n ): void {\n // Match original litegraph LINEAR_LINK mode with 4-point path\n const l = 15 // offset distance for control points\n\n const innerA = { x: start.x, y: start.y }\n const innerB = { x: end.x, y: end.y }\n\n // Apply directional offsets to create control points\n switch (startDir) {\n case 'left':\n innerA.x -= l\n break\n case 'right':\n innerA.x += l\n break\n case 'up':\n innerA.y -= l\n break\n case 'down':\n innerA.y += l\n break\n case 'none':\n break\n }\n\n switch (endDir) {\n case 'left':\n innerB.x -= l\n break\n case 'right':\n innerB.x += l\n break\n case 'up':\n innerB.y -= l\n break\n case 'down':\n innerB.y += l\n break\n case 'none':\n break\n }\n\n // Draw 4-point path: start -> innerA -> innerB -> end\n path.moveTo(start.x, start.y)\n path.lineTo(innerA.x, innerA.y)\n path.lineTo(innerB.x, innerB.y)\n path.lineTo(end.x, end.y)\n }\n\n private buildStraightPath(\n path: Path2D,\n start: Point,\n end: Point,\n startDir: Direction,\n endDir: Direction\n ): void {\n // Match original STRAIGHT_LINK implementation with l=10 offset\n const l = 10 // offset distance matching original\n\n const innerA = { x: start.x, y: start.y }\n const innerB = { x: end.x, y: end.y }\n\n // Apply directional offsets to match original behavior\n switch (startDir) {\n case 'left':\n innerA.x -= l\n break\n case 'right':\n innerA.x += l\n break\n case 'up':\n innerA.y -= l\n break\n case 'down':\n innerA.y += l\n break\n case 'none':\n break\n }\n\n switch (endDir) {\n case 'left':\n innerB.x -= l\n break\n case 'right':\n innerB.x += l\n break\n case 'up':\n innerB.y -= l\n break\n case 'down':\n innerB.y += l\n break\n case 'none':\n break\n }\n\n // Calculate midpoint using innerA/innerB positions (matching original)\n const midX = (innerA.x + innerB.x) * 0.5\n\n // Build path: start -> innerA -> (midX, innerA.y) -> (midX, innerB.y) -> innerB -> end\n path.moveTo(start.x, start.y)\n path.lineTo(innerA.x, innerA.y)\n path.lineTo(midX, innerA.y)\n path.lineTo(midX, innerB.y)\n path.lineTo(innerB.x, innerB.y)\n path.lineTo(end.x, end.y)\n }\n\n private buildSplinePath(\n path: Path2D,\n start: Point,\n end: Point,\n startDir: Direction,\n endDir: Direction,\n controlPoints?: Point[]\n ): void {\n path.moveTo(start.x, start.y)\n\n // Calculate control points if not provided\n const controls =\n controlPoints || this.calculateControlPoints(start, end, startDir, endDir)\n\n if (controls.length >= 2) {\n // Cubic bezier\n path.bezierCurveTo(\n controls[0].x,\n controls[0].y,\n controls[1].x,\n controls[1].y,\n end.x,\n end.y\n )\n } else if (controls.length === 1) {\n // Quadratic bezier\n path.quadraticCurveTo(controls[0].x, controls[0].y, end.x, end.y)\n } else {\n // Fallback to linear\n path.lineTo(end.x, end.y)\n }\n }\n\n private calculateControlPoints(\n start: Point,\n end: Point,\n startDir: Direction,\n endDir: Direction\n ): Point[] {\n const dist = Math.sqrt(\n Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)\n )\n const controlDist = Math.max(30, dist * 0.25)\n\n // Calculate control point offsets based on direction\n const startControl = this.getDirectionOffset(startDir, controlDist)\n const endControl = this.getDirectionOffset(endDir, controlDist)\n\n return [\n { x: start.x + startControl.x, y: start.y + startControl.y },\n { x: end.x + endControl.x, y: end.y + endControl.y }\n ]\n }\n\n private getDirectionOffset(direction: Direction, distance: number): Point {\n switch (direction) {\n case 'left':\n return { x: -distance, y: 0 }\n case 'right':\n return { x: distance, y: 0 }\n case 'up':\n return { x: 0, y: -distance }\n case 'down':\n return { x: 0, y: distance }\n case 'none':\n default:\n return { x: 0, y: 0 }\n }\n }\n\n private drawArrows(\n ctx: CanvasRenderingContext2D,\n link: LinkRenderData,\n context: RenderContext,\n color: string\n ): void {\n if (!context.style.showArrows) return\n\n // Render arrows at 0.25 and 0.75 positions along the path (matching original)\n const positions = [0.25, 0.75]\n\n for (const t of positions) {\n // Compute arrow position and angle\n const posA = this.computeConnectionPoint(link, t, context)\n const posB = this.computeConnectionPoint(link, t + 0.01, context) // slightly ahead for angle\n\n const angle = Math.atan2(posB.y - posA.y, posB.x - posA.x)\n\n // Draw arrow triangle (matching original shape)\n const transform = ctx.getTransform()\n ctx.translate(posA.x, posA.y)\n ctx.rotate(angle)\n ctx.fillStyle = color\n ctx.beginPath()\n ctx.moveTo(-5, -3)\n ctx.lineTo(0, +7)\n ctx.lineTo(+5, -3)\n ctx.fill()\n ctx.setTransform(transform)\n }\n }\n\n /**\n * Compute a point along the link path at position t (0 to 1)\n * For backward compatibility with original litegraph, this always uses\n * bezier calculation with spline offsets, regardless of render mode.\n * This ensures arrow positions match the original implementation.\n */\n private computeConnectionPoint(\n link: LinkRenderData,\n t: number,\n _context: RenderContext\n ): Point {\n const { startPoint, endPoint, startDirection, endDirection } = link\n\n // Match original behavior: always use bezier math with spline offsets\n // regardless of render mode (for arrow position compatibility)\n const dist = Math.sqrt(\n Math.pow(endPoint.x - startPoint.x, 2) +\n Math.pow(endPoint.y - startPoint.y, 2)\n )\n const factor = 0.25\n\n // Create control points with spline offsets (matching original #addSplineOffset)\n const pa = { x: startPoint.x, y: startPoint.y }\n const pb = { x: endPoint.x, y: endPoint.y }\n\n // Apply spline offsets based on direction\n switch (startDirection) {\n case 'left':\n pa.x -= dist * factor\n break\n case 'right':\n pa.x += dist * factor\n break\n case 'up':\n pa.y -= dist * factor\n break\n case 'down':\n pa.y += dist * factor\n break\n case 'none':\n break\n }\n\n switch (endDirection) {\n case 'left':\n pb.x -= dist * factor\n break\n case 'right':\n pb.x += dist * factor\n break\n case 'up':\n pb.y -= dist * factor\n break\n case 'down':\n pb.y += dist * factor\n break\n case 'none':\n break\n }\n\n // Calculate bezier point (matching original computeConnectionPoint)\n const c1 = (1 - t) * (1 - t) * (1 - t)\n const c2 = 3 * ((1 - t) * (1 - t)) * t\n const c3 = 3 * (1 - t) * (t * t)\n const c4 = t * t * t\n\n return {\n x: c1 * startPoint.x + c2 * pa.x + c3 * pb.x + c4 * endPoint.x,\n y: c1 * startPoint.y + c2 * pa.y + c3 * pb.y + c4 * endPoint.y\n }\n }\n\n private drawFlowAnimation(\n ctx: CanvasRenderingContext2D,\n _path: Path2D,\n link: LinkRenderData,\n context: RenderContext\n ): void {\n if (!context.animation) return\n\n // Match original implementation: render 5 moving circles along the path\n const time = context.animation.time\n const linkColor = this.determineLinkColor(link, context, false)\n\n ctx.save()\n ctx.fillStyle = linkColor\n\n // Draw 5 circles at different positions along the path\n for (let i = 0; i < 5; ++i) {\n // Calculate position along path (0 to 1), with time-based animation\n const f = (time + i * 0.2) % 1\n const flowPos = this.computeConnectionPoint(link, f, context)\n\n // Draw circle at this position\n ctx.beginPath()\n ctx.arc(flowPos.x, flowPos.y, 5, 0, 2 * Math.PI)\n ctx.fill()\n }\n\n ctx.restore()\n }\n\n /**\n * Utility to find a point on a bezier curve (for hit detection)\n */\n findPointOnBezier(\n t: number,\n p0: Point,\n p1: Point,\n p2: Point,\n p3: Point\n ): Point {\n const mt = 1 - t\n const mt2 = mt * mt\n const mt3 = mt2 * mt\n const t2 = t * t\n const t3 = t2 * t\n\n return {\n x: mt3 * p0.x + 3 * mt2 * t * p1.x + 3 * mt * t2 * p2.x + t3 * p3.x,\n y: mt3 * p0.y + 3 * mt2 * t * p1.y + 3 * mt * t2 * p2.y + t3 * p3.y\n }\n }\n\n /**\n * Draw a link being dragged from a slot to the mouse position\n * Returns a Path2D object for potential hit detection\n */\n drawDraggingLink(\n ctx: CanvasRenderingContext2D,\n dragData: DragLinkData,\n context: RenderContext\n ): Path2D {\n // Create LinkRenderData from drag data\n // When dragging from input, swap the points/directions\n const linkData: LinkRenderData = dragData.fromInput\n ? {\n id: 'dragging',\n startPoint: dragData.dragPoint,\n endPoint: dragData.fixedPoint,\n startDirection:\n dragData.dragDirection ||\n this.getOppositeDirection(dragData.fixedDirection),\n endDirection: dragData.fixedDirection,\n color: dragData.color,\n type: dragData.type,\n disabled: dragData.disabled\n }\n : {\n id: 'dragging',\n startPoint: dragData.fixedPoint,\n endPoint: dragData.dragPoint,\n startDirection: dragData.fixedDirection,\n endDirection:\n dragData.dragDirection ||\n this.getOppositeDirection(dragData.fixedDirection),\n color: dragData.color,\n type: dragData.type,\n disabled: dragData.disabled\n }\n\n // Use standard link drawing\n return this.drawLink(ctx, linkData, context)\n }\n\n /**\n * Get the opposite direction (for drag preview)\n */\n private getOppositeDirection(direction: Direction): Direction {\n switch (direction) {\n case 'left':\n return 'right'\n case 'right':\n return 'left'\n case 'up':\n return 'down'\n case 'down':\n return 'up'\n case 'none':\n default:\n return 'none'\n }\n }\n\n /**\n * Get the center point of a link (useful for labels, debugging)\n */\n getLinkCenter(link: LinkRenderData): Point {\n // For now, simple midpoint\n // Could be enhanced to find actual curve midpoint\n return {\n x: (link.startPoint.x + link.endPoint.x) / 2,\n y: (link.startPoint.y + link.endPoint.y) / 2\n }\n }\n\n /**\n * Calculate and store the center point and angle of a link\n * Mimics the original litegraph center point calculation\n */\n private calculateCenterPoint(\n link: LinkRenderData,\n context: RenderContext\n ): void {\n const { startPoint, endPoint, controlPoints } = link\n\n if (\n context.style.mode === 'spline' &&\n controlPoints &&\n controlPoints.length >= 2\n ) {\n // For spline mode, find point at t=0.5 on the bezier curve\n const centerPos = this.findPointOnBezier(\n 0.5,\n startPoint,\n controlPoints[0],\n controlPoints[1],\n endPoint\n )\n link.centerPos = centerPos\n\n // Calculate angle for arrow marker (point slightly past center)\n if (context.style.centerMarkerShape === 'arrow') {\n const justPastCenter = this.findPointOnBezier(\n 0.51,\n startPoint,\n controlPoints[0],\n controlPoints[1],\n endPoint\n )\n link.centerAngle = Math.atan2(\n justPastCenter.y - centerPos.y,\n justPastCenter.x - centerPos.x\n )\n }\n } else if (context.style.mode === 'linear') {\n // For linear mode, calculate midpoint between control points (matching original)\n const l = 15 // Same offset as buildLinearPath\n const innerA = { x: startPoint.x, y: startPoint.y }\n const innerB = { x: endPoint.x, y: endPoint.y }\n\n // Apply same directional offsets as buildLinearPath\n switch (link.startDirection) {\n case 'left':\n innerA.x -= l\n break\n case 'right':\n innerA.x += l\n break\n case 'up':\n innerA.y -= l\n break\n case 'down':\n innerA.y += l\n break\n }\n\n switch (link.endDirection) {\n case 'left':\n innerB.x -= l\n break\n case 'right':\n innerB.x += l\n break\n case 'up':\n innerB.y -= l\n break\n case 'down':\n innerB.y += l\n break\n }\n\n link.centerPos = {\n x: (innerA.x + innerB.x) * 0.5,\n y: (innerA.y + innerB.y) * 0.5\n }\n\n if (context.style.centerMarkerShape === 'arrow') {\n link.centerAngle = Math.atan2(innerB.y - innerA.y, innerB.x - innerA.x)\n }\n } else if (context.style.mode === 'straight') {\n // For straight mode, match original STRAIGHT_LINK center calculation\n const l = 10 // Same offset as buildStraightPath\n const innerA = { x: startPoint.x, y: startPoint.y }\n const innerB = { x: endPoint.x, y: endPoint.y }\n\n // Apply same directional offsets as buildStraightPath\n switch (link.startDirection) {\n case 'left':\n innerA.x -= l\n break\n case 'right':\n innerA.x += l\n break\n case 'up':\n innerA.y -= l\n break\n case 'down':\n innerA.y += l\n break\n }\n\n switch (link.endDirection) {\n case 'left':\n innerB.x -= l\n break\n case 'right':\n innerB.x += l\n break\n case 'up':\n innerB.y -= l\n break\n case 'down':\n innerB.y += l\n break\n }\n\n // Calculate center using midX and average of innerA/innerB y positions\n const midX = (innerA.x + innerB.x) * 0.5\n link.centerPos = {\n x: midX,\n y: (innerA.y + innerB.y) * 0.5\n }\n\n if (context.style.centerMarkerShape === 'arrow') {\n const diff = innerB.y - innerA.y\n if (Math.abs(diff) < 4) {\n link.centerAngle = 0\n } else if (diff > 0) {\n link.centerAngle = Math.PI * 0.5\n } else {\n link.centerAngle = -(Math.PI * 0.5)\n }\n }\n } else {\n // Fallback to simple midpoint\n link.centerPos = this.getLinkCenter(link)\n if (context.style.centerMarkerShape === 'arrow') {\n link.centerAngle = Math.atan2(\n endPoint.y - startPoint.y,\n endPoint.x - startPoint.x\n )\n }\n }\n }\n\n /**\n * Draw the center marker on a link (for menu interaction)\n * Matches the original litegraph center marker rendering\n */\n private drawCenterMarker(\n ctx: CanvasRenderingContext2D,\n link: LinkRenderData,\n context: RenderContext,\n color: string\n ): void {\n if (!link.centerPos) return\n\n ctx.beginPath()\n\n if (\n context.style.centerMarkerShape === 'arrow' &&\n link.centerAngle !== undefined\n ) {\n const transform = ctx.getTransform()\n ctx.translate(link.centerPos.x, link.centerPos.y)\n ctx.rotate(link.centerAngle)\n // The math is off, but it currently looks better in chromium (from original)\n ctx.moveTo(-3.2, -5)\n ctx.lineTo(7, 0)\n ctx.lineTo(-3.2, 5)\n ctx.setTransform(transform)\n } else {\n // Default to circle\n ctx.arc(link.centerPos.x, link.centerPos.y, 5, 0, Math.PI * 2)\n }\n\n // Apply disabled pattern or color\n if (link.disabled && context.patterns?.disabled) {\n const { fillStyle, globalAlpha } = ctx\n ctx.fillStyle = context.patterns.disabled\n ctx.globalAlpha = 0.75\n ctx.fill()\n ctx.globalAlpha = globalAlpha\n ctx.fillStyle = fillStyle\n } else {\n ctx.fillStyle = color\n ctx.fill()\n }\n }\n}\n","/**\n * Litegraph Link Adapter\n *\n * Bridges the gap between litegraph's data model and the pure canvas renderer.\n * Converts litegraph-specific types (LLink, LGraphNode, slots) into generic\n * rendering data that can be consumed by the PathRenderer.\n * Maintains backward compatibility with existing litegraph integration.\n */\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CanvasColour, Point } from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport {\n LinkDirection,\n LinkMarkerShape,\n LinkRenderType\n} from '@/lib/litegraph/src/types/globalEnums'\nimport { CanvasPathRenderer } from '@/renderer/core/canvas/pathRenderer'\nimport type {\n ArrowShape,\n Direction,\n LinkRenderData,\n RenderContext as PathRenderContext,\n Point as PointObj,\n RenderMode\n} from '@/renderer/core/canvas/pathRenderer'\nimport { layoutStore } from '@/renderer/core/layout/store/layoutStore'\nimport type { Bounds } from '@/renderer/core/layout/types'\n\nexport interface LinkRenderContext {\n // Canvas settings\n renderMode: LinkRenderType\n connectionWidth: number\n renderBorder: boolean\n lowQuality: boolean\n highQualityRender: boolean\n scale: number\n linkMarkerShape: LinkMarkerShape\n renderConnectionArrows: boolean\n\n // State\n highlightedLinks: Set<string | number>\n\n // Colors\n defaultLinkColor: CanvasColour\n linkTypeColors: Record<string, CanvasColour>\n\n // Pattern for disabled links (optional)\n disabledPattern?: CanvasPattern | null\n}\n\nexport class LitegraphLinkAdapter {\n private readonly pathRenderer = new CanvasPathRenderer()\n\n constructor(public readonly enableLayoutStoreWrites = true) {}\n\n /**\n * Convert LinkDirection enum to Direction string\n */\n private convertDirection(dir: LinkDirection): Direction {\n switch (dir) {\n case LinkDirection.LEFT:\n return 'left'\n case LinkDirection.RIGHT:\n return 'right'\n case LinkDirection.UP:\n return 'up'\n case LinkDirection.DOWN:\n return 'down'\n case LinkDirection.CENTER:\n return 'none'\n default:\n return 'right'\n }\n }\n\n /**\n * Convert LinkRenderContext to PathRenderContext\n */\n private convertToPathRenderContext(\n context: LinkRenderContext\n ): PathRenderContext {\n // Match original arrow rendering conditions:\n // Arrows only render when scale >= 0.6 AND highquality_render AND render_connection_arrows\n const shouldShowArrows =\n context.scale >= 0.6 &&\n context.highQualityRender &&\n context.renderConnectionArrows\n\n // Only show center marker when not set to None\n const shouldShowCenterMarker =\n context.linkMarkerShape !== LinkMarkerShape.None\n\n return {\n style: {\n mode: this.convertRenderMode(context.renderMode),\n connectionWidth: context.connectionWidth,\n borderWidth: context.renderBorder ? 4 : undefined,\n arrowShape: this.convertArrowShape(context.linkMarkerShape),\n showArrows: shouldShowArrows,\n lowQuality: context.lowQuality,\n // Center marker settings (matches original litegraph behavior)\n showCenterMarker: shouldShowCenterMarker,\n centerMarkerShape:\n context.linkMarkerShape === LinkMarkerShape.Arrow\n ? 'arrow'\n : 'circle',\n highQuality: context.highQualityRender\n },\n colors: {\n default: String(context.defaultLinkColor),\n byType: this.convertColorMap(context.linkTypeColors),\n highlighted: '#FFF'\n },\n patterns: {\n disabled: context.disabledPattern\n },\n animation: {\n time: LiteGraph.getTime() * 0.001\n },\n scale: context.scale,\n highlightedIds: new Set(Array.from(context.highlightedLinks).map(String))\n }\n }\n\n /**\n * Convert LinkRenderType to RenderMode\n */\n private convertRenderMode(mode: LinkRenderType): RenderMode {\n switch (mode) {\n case LinkRenderType.LINEAR_LINK:\n return 'linear'\n case LinkRenderType.STRAIGHT_LINK:\n return 'straight'\n case LinkRenderType.SPLINE_LINK:\n default:\n return 'spline'\n }\n }\n\n /**\n * Convert LinkMarkerShape to ArrowShape\n */\n private convertArrowShape(shape: LinkMarkerShape): ArrowShape {\n switch (shape) {\n case LinkMarkerShape.Circle:\n return 'circle'\n case LinkMarkerShape.Arrow:\n default:\n return 'triangle'\n }\n }\n\n /**\n * Convert color map to ensure all values are strings\n */\n private convertColorMap(\n colors: Record<string, CanvasColour>\n ): Record<string, string> {\n const result: Record<string, string> = {}\n for (const [key, value] of Object.entries(colors)) {\n result[key] = String(value)\n }\n return result\n }\n\n /**\n * Apply spline offset to a point, mimicking original #addSplineOffset behavior\n * Critically: does nothing for CENTER/NONE directions (no case for them)\n */\n private applySplineOffset(\n point: PointObj,\n direction: LinkDirection,\n distance: number\n ): void {\n switch (direction) {\n case LinkDirection.LEFT:\n point.x -= distance\n break\n case LinkDirection.RIGHT:\n point.x += distance\n break\n case LinkDirection.UP:\n point.y -= distance\n break\n case LinkDirection.DOWN:\n point.y += distance\n break\n // CENTER and NONE: no offset applied (original behavior)\n }\n }\n\n /**\n * Direct rendering method compatible with LGraphCanvas\n * Converts data and delegates to pure renderer\n */\n renderLinkDirect(\n ctx: CanvasRenderingContext2D,\n a: Readonly<Point>,\n b: Readonly<Point>,\n link: LLink | null,\n skip_border: boolean,\n flow: number | boolean | null,\n color: CanvasColour | null,\n start_dir: LinkDirection,\n end_dir: LinkDirection,\n context: LinkRenderContext,\n extras: {\n reroute?: Reroute\n startControl?: Readonly<Point>\n endControl?: Readonly<Point>\n num_sublines?: number\n disabled?: boolean\n } = {}\n ): void {\n // Apply same defaults as original renderLink\n const startDir = start_dir || LinkDirection.RIGHT\n const endDir = end_dir || LinkDirection.LEFT\n\n // Convert flow to boolean\n const flowBool = flow === true || (typeof flow === 'number' && flow > 0)\n\n // Create LinkRenderData from direct parameters\n const linkData: LinkRenderData = {\n id: link ? String(link.id) : 'temp',\n startPoint: { x: a[0], y: a[1] },\n endPoint: { x: b[0], y: b[1] },\n startDirection: this.convertDirection(startDir),\n endDirection: this.convertDirection(endDir),\n color: color !== null && color !== undefined ? String(color) : undefined,\n type: link?.type !== undefined ? String(link.type) : undefined,\n flow: flowBool,\n disabled: extras.disabled || false\n }\n\n // Control points handling (spline mode):\n // - Pre-refactor, the old renderLink honored a single provided control and\n // derived the missing side via #addSplineOffset (CENTER => no offset).\n // - Restore that behavior here so reroute segments render identically.\n if (context.renderMode === LinkRenderType.SPLINE_LINK) {\n const hasStartCtrl = !!extras.startControl\n const hasEndCtrl = !!extras.endControl\n\n // Compute distance once for offsets\n const dist = Math.sqrt(\n (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])\n )\n const factor = 0.25\n\n const cps: PointObj[] = []\n\n if (hasStartCtrl && hasEndCtrl) {\n // Both provided explicitly\n cps.push(\n {\n x: a[0] + (extras.startControl![0] || 0),\n y: a[1] + (extras.startControl![1] || 0)\n },\n {\n x: b[0] + (extras.endControl![0] || 0),\n y: b[1] + (extras.endControl![1] || 0)\n }\n )\n linkData.controlPoints = cps\n } else if (hasStartCtrl && !hasEndCtrl) {\n // Start provided, derive end via direction offset (CENTER => no offset)\n const start = {\n x: a[0] + (extras.startControl![0] || 0),\n y: a[1] + (extras.startControl![1] || 0)\n }\n const end = { x: b[0], y: b[1] }\n this.applySplineOffset(end, endDir, dist * factor)\n cps.push(start, end)\n linkData.controlPoints = cps\n } else if (!hasStartCtrl && hasEndCtrl) {\n // End provided, derive start via direction offset (CENTER => no offset)\n const start = { x: a[0], y: a[1] }\n this.applySplineOffset(start, startDir, dist * factor)\n const end = {\n x: b[0] + (extras.endControl![0] || 0),\n y: b[1] + (extras.endControl![1] || 0)\n }\n cps.push(start, end)\n linkData.controlPoints = cps\n } else {\n // Neither provided: derive both from directions (CENTER => no offset)\n const start = { x: a[0], y: a[1] }\n const end = { x: b[0], y: b[1] }\n this.applySplineOffset(start, startDir, dist * factor)\n this.applySplineOffset(end, endDir, dist * factor)\n cps.push(start, end)\n linkData.controlPoints = cps\n }\n }\n\n // Convert context\n const pathContext = this.convertToPathRenderContext(context)\n\n // Override skip_border if needed\n if (skip_border) {\n pathContext.style.borderWidth = undefined\n }\n\n // Render using pure renderer\n const path = this.pathRenderer.drawLink(ctx, linkData, pathContext)\n\n // Store path for hit detection\n const linkSegment = extras.reroute ?? link\n if (linkSegment) {\n linkSegment.path = path\n\n // Copy calculated center position back to litegraph object\n // This is needed for hit detection and menu interaction\n if (linkData.centerPos) {\n linkSegment._pos = linkSegment._pos || [0, 0]\n linkSegment._pos[0] = linkData.centerPos.x\n linkSegment._pos[1] = linkData.centerPos.y\n\n // Store center angle if calculated (for arrow markers)\n if (linkData.centerAngle !== undefined) {\n linkSegment._centreAngle = linkData.centerAngle\n }\n }\n\n // Update layout store when writes are enabled (event-driven path)\n if (this.enableLayoutStoreWrites && link && link.id !== -1) {\n // Calculate bounds and center only when writing\n const bounds = this.calculateLinkBounds(\n [linkData.startPoint.x, linkData.startPoint.y] as Readonly<Point>,\n [linkData.endPoint.x, linkData.endPoint.y] as Readonly<Point>,\n linkData\n )\n const centerPos = linkData.centerPos || {\n x: (linkData.startPoint.x + linkData.endPoint.x) / 2,\n y: (linkData.startPoint.y + linkData.endPoint.y) / 2\n }\n\n // Update whole link layout (only if not a reroute segment)\n if (!extras.reroute) {\n layoutStore.updateLinkLayout(link.id, {\n id: link.id,\n path: path,\n bounds: bounds,\n centerPos: centerPos,\n sourceNodeId: String(link.origin_id),\n targetNodeId: String(link.target_id),\n sourceSlot: link.origin_slot,\n targetSlot: link.target_slot\n })\n }\n\n // Always update segment layout (for both regular links and reroute segments)\n const rerouteId = extras.reroute ? extras.reroute.id : null\n layoutStore.updateLinkSegmentLayout(link.id, rerouteId, {\n path: path,\n bounds: bounds,\n centerPos: centerPos\n })\n }\n }\n }\n\n renderDraggingLink(\n ctx: CanvasRenderingContext2D,\n from: Readonly<Point>,\n to: Readonly<Point>,\n colour: CanvasColour,\n startDir: LinkDirection,\n endDir: LinkDirection,\n context: LinkRenderContext\n ): void {\n this.renderLinkDirect(\n ctx,\n from,\n to,\n null,\n false,\n null,\n colour,\n startDir,\n endDir,\n {\n ...context,\n linkMarkerShape: LinkMarkerShape.None\n },\n {\n disabled: false\n }\n )\n }\n\n /**\n * Calculate bounding box for a link\n * Includes padding for line width and control points\n */\n private calculateLinkBounds(\n startPos: Readonly<Point>,\n endPos: Readonly<Point>,\n linkData: LinkRenderData\n ): Bounds {\n let minX = Math.min(startPos[0], endPos[0])\n let maxX = Math.max(startPos[0], endPos[0])\n let minY = Math.min(startPos[1], endPos[1])\n let maxY = Math.max(startPos[1], endPos[1])\n\n // Include control points if they exist (for spline links)\n if (linkData.controlPoints) {\n for (const cp of linkData.controlPoints) {\n minX = Math.min(minX, cp.x)\n maxX = Math.max(maxX, cp.x)\n minY = Math.min(minY, cp.y)\n maxY = Math.max(maxY, cp.y)\n }\n }\n\n // Add padding for line width and hit tolerance\n const padding = 20\n\n return {\n x: minX - padding,\n y: minY - padding,\n width: maxX - minX + 2 * padding,\n height: maxY - minY + 2 * padding\n }\n }\n}\n","import type {\n IWidgetInputSlot,\n SharedIntersection\n} from '@/lib/litegraph/src/interfaces'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n INodeSlot,\n IWidget\n} from '@/lib/litegraph/src/litegraph'\nimport type {\n ISerialisableNodeInput,\n ISerialisableNodeOutput\n} from '@/lib/litegraph/src/types/serialisation'\n\ntype CommonIoSlotProps = SharedIntersection<\n ISerialisableNodeInput,\n ISerialisableNodeOutput\n>\n\nfunction shallowCloneCommonProps(slot: CommonIoSlotProps): CommonIoSlotProps {\n const {\n color_off,\n color_on,\n dir,\n label,\n localized_name,\n locked,\n name,\n nameLocked,\n removable,\n shape,\n type\n } = slot\n return {\n color_off,\n color_on,\n dir,\n label,\n localized_name,\n locked,\n name,\n nameLocked,\n removable,\n shape,\n type\n }\n}\n\nexport function inputAsSerialisable(\n slot: INodeInputSlot\n): ISerialisableNodeInput {\n const { link } = slot\n const widgetOrPos = slot.widget\n ? { widget: { name: slot.widget.name } }\n : { pos: slot.pos }\n\n return {\n ...shallowCloneCommonProps(slot),\n ...widgetOrPos,\n link\n }\n}\n\nexport function outputAsSerialisable(\n slot: INodeOutputSlot & { widget?: IWidget }\n): ISerialisableNodeOutput {\n const { pos, slot_index, links, widget } = slot\n // Output widgets do not exist in Litegraph; this is a temporary downstream workaround.\n const outputWidget = widget ? { widget: { name: widget.name } } : null\n\n return {\n ...shallowCloneCommonProps(slot),\n ...outputWidget,\n pos,\n slot_index,\n links\n }\n}\n\nexport function isINodeInputSlot(slot: INodeSlot): slot is INodeInputSlot {\n return 'link' in slot\n}\n\n/**\n * Type guard: Whether this input slot is attached to a widget.\n * @param slot The slot to check.\n */\n\nexport function isWidgetInputSlot(\n slot: INodeInputSlot\n): slot is IWidgetInputSlot {\n return !!slot.widget\n}\n","/**\n * Slot identifier utilities for consistent slot key generation and parsing\n *\n * Provides a centralized interface for slot identification across the layout system\n *\n * @TODO Replace this concatenated string with root cause fix\n */\n\ninterface SlotIdentifier {\n nodeId: string\n index: number\n isInput: boolean\n}\n\n/**\n * Generate a unique key for a slot\n * Format: \"{nodeId}-{in|out}-{index}\"\n */\nexport function getSlotKey(identifier: SlotIdentifier): string\nexport function getSlotKey(\n nodeId: string,\n index: number,\n isInput: boolean\n): string\nexport function getSlotKey(\n nodeIdOrIdentifier: string | SlotIdentifier,\n index?: number,\n isInput?: boolean\n): string {\n if (typeof nodeIdOrIdentifier === 'object') {\n const { nodeId, index, isInput } = nodeIdOrIdentifier\n return `${nodeId}-${isInput ? 'in' : 'out'}-${index}`\n }\n\n if (index === undefined || isInput === undefined) {\n throw new Error('Missing required parameters for slot key generation')\n }\n\n return `${nodeIdOrIdentifier}-${isInput ? 'in' : 'out'}-${index}`\n}\n","/**\n * Slot Position Calculations\n *\n * Centralized utility for calculating input/output slot positions on nodes.\n * This allows both litegraph nodes and the layout system to use the same\n * calculation logic while providing their own position data.\n */\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport { isWidgetInputSlot } from '@/lib/litegraph/src/node/slotUtils'\nimport { getSlotKey } from '@/renderer/core/layout/slots/slotIdentifier'\nimport { layoutStore } from '@/renderer/core/layout/store/layoutStore'\n\nexport interface SlotPositionContext {\n /** Node's X position in graph coordinates */\n nodeX: number\n /** Node's Y position in graph coordinates */\n nodeY: number\n /** Node's width */\n nodeWidth: number\n /** Node's height */\n nodeHeight: number\n /** Whether the node is collapsed */\n collapsed: boolean\n /** Collapsed width (if applicable) */\n collapsedWidth?: number\n /** Node constructor's slot_start_y offset */\n slotStartY?: number\n /** Node's input slots */\n inputs: INodeInputSlot[]\n /** Node's output slots */\n outputs: INodeOutputSlot[]\n /** Node's widgets (for widget slot detection) */\n widgets?: Array<{ name?: string }>\n}\n\n/**\n * Calculate the position of an input slot in graph coordinates\n * @param context Node context containing position and slot data\n * @param slot The input slot index\n * @returns Position of the input slot center in graph coordinates\n */\nfunction calculateInputSlotPos(\n context: SlotPositionContext,\n slot: number\n): Point {\n const input = context.inputs[slot]\n if (!input) return [context.nodeX, context.nodeY]\n\n return calculateInputSlotPosFromSlot(context, input)\n}\n\n/**\n * Calculate the position of an input slot in graph coordinates\n * @param context Node context containing position and slot data\n * @param input The input slot object\n * @returns Position of the input slot center in graph coordinates\n */\nexport function calculateInputSlotPosFromSlot(\n context: SlotPositionContext,\n input: INodeInputSlot\n): Point {\n const { nodeX, nodeY, collapsed } = context\n\n // Handle collapsed nodes\n if (collapsed) {\n const halfTitle = LiteGraph.NODE_TITLE_HEIGHT * 0.5\n return [nodeX, nodeY - halfTitle]\n }\n\n // Handle hard-coded positions\n const { pos } = input\n if (pos) return [nodeX + pos[0], nodeY + pos[1]]\n\n // Default vertical slots\n const offsetX = LiteGraph.NODE_SLOT_HEIGHT * 0.5\n const nodeOffsetY = context.slotStartY || 0\n const defaultVerticalInputs = getDefaultVerticalInputs(context)\n const slotIndex = defaultVerticalInputs.indexOf(input)\n const slotY = (slotIndex + 0.7) * LiteGraph.NODE_SLOT_HEIGHT\n\n return [nodeX + offsetX, nodeY + slotY + nodeOffsetY]\n}\n\n/**\n * Calculate the position of an output slot in graph coordinates\n * @param context Node context containing position and slot data\n * @param slot The output slot index\n * @returns Position of the output slot center in graph coordinates\n */\nfunction calculateOutputSlotPos(\n context: SlotPositionContext,\n slot: number\n): Point {\n const { nodeX, nodeY, nodeWidth, collapsed, collapsedWidth, outputs } =\n context\n\n // Handle collapsed nodes\n if (collapsed) {\n const width = collapsedWidth || LiteGraph.NODE_COLLAPSED_WIDTH\n const halfTitle = LiteGraph.NODE_TITLE_HEIGHT * 0.5\n return [nodeX + width, nodeY - halfTitle]\n }\n\n const outputSlot = outputs[slot]\n if (!outputSlot) return [nodeX + nodeWidth, nodeY]\n\n // Handle hard-coded positions\n const outputPos = outputSlot.pos\n if (outputPos) return [nodeX + outputPos[0], nodeY + outputPos[1]]\n\n // Default vertical slots\n const offsetX = LiteGraph.NODE_SLOT_HEIGHT * 0.5\n const nodeOffsetY = context.slotStartY || 0\n const defaultVerticalOutputs = getDefaultVerticalOutputs(context)\n const slotIndex = defaultVerticalOutputs.indexOf(outputSlot)\n const slotY = (slotIndex + 0.7) * LiteGraph.NODE_SLOT_HEIGHT\n\n // TODO: Why +1?\n return [nodeX + nodeWidth + 1 - offsetX, nodeY + slotY + nodeOffsetY]\n}\n\n/**\n * Get slot position using layout tree if available, fallback to node's position\n * Unified implementation used by both LitegraphLinkAdapter and useLinkLayoutSync\n * @param node The LGraphNode\n * @param slotIndex The slot index\n * @param isInput Whether this is an input slot\n * @returns Position of the slot center in graph coordinates\n */\nexport function getSlotPosition(\n node: LGraphNode,\n slotIndex: number,\n isInput: boolean\n): Point {\n // Only use DOM-registered slot positions when Vue nodes mode is enabled\n if (LiteGraph.vueNodesMode) {\n // Try to get precise position from slot layout (DOM-registered)\n const slotKey = getSlotKey(String(node.id), slotIndex, isInput)\n const slotLayout = layoutStore.getSlotLayout(slotKey)\n if (slotLayout) {\n return [slotLayout.position.x, slotLayout.position.y]\n }\n\n // Fallback: derive position from node layout tree and slot model\n const nodeLayout = layoutStore.getNodeLayoutRef(String(node.id)).value\n\n if (nodeLayout) {\n // Create context from layout tree data\n const context: SlotPositionContext = {\n nodeX: nodeLayout.position.x,\n nodeY: nodeLayout.position.y,\n nodeWidth: nodeLayout.size.width,\n nodeHeight: nodeLayout.size.height,\n collapsed: node.flags.collapsed || false,\n collapsedWidth: node._collapsed_width,\n slotStartY: node.constructor.slot_start_y,\n inputs: node.inputs,\n outputs: node.outputs,\n widgets: node.widgets\n }\n\n // Use helper to calculate position\n return isInput\n ? calculateInputSlotPos(context, slotIndex)\n : calculateOutputSlotPos(context, slotIndex)\n }\n }\n\n // Fallback: calculate directly from node properties (legacy litegraph behavior)\n const context: SlotPositionContext = {\n nodeX: node.pos[0],\n nodeY: node.pos[1],\n nodeWidth: node.size[0],\n nodeHeight: node.size[1],\n collapsed: node.flags.collapsed || false,\n collapsedWidth: node._collapsed_width,\n slotStartY: node.constructor.slot_start_y,\n inputs: node.inputs,\n outputs: node.outputs,\n widgets: node.widgets\n }\n\n return isInput\n ? calculateInputSlotPos(context, slotIndex)\n : calculateOutputSlotPos(context, slotIndex)\n}\n\n/**\n * Get the inputs that are not positioned with absolute coordinates\n */\nfunction getDefaultVerticalInputs(\n context: SlotPositionContext\n): INodeInputSlot[] {\n return context.inputs.filter(\n (slot) => !slot.pos && !(context.widgets?.length && isWidgetInputSlot(slot))\n )\n}\n\n/**\n * Get the outputs that are not positioned with absolute coordinates\n */\nfunction getDefaultVerticalOutputs(\n context: SlotPositionContext\n): INodeOutputSlot[] {\n return context.outputs.filter((slot) => !slot.pos)\n}\n","import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'\n\n/**\n * A globally unique identifier for nodes that maintains consistency across\n * multiple instances of the same subgraph.\n *\n * Format:\n * - For subgraph nodes: `<immediate-contained-subgraph-uuid>:<local-node-id>`\n * - For root graph nodes: `<local-node-id>`\n *\n * Examples:\n * - \"a1b2c3d4-e5f6-7890-abcd-ef1234567890:123\" (node in subgraph)\n * - \"456\" (node in root graph)\n *\n * Unlike execution IDs which change based on the instance path,\n * NodeLocatorId remains the same for all instances of a particular node.\n */\nexport type NodeLocatorId = string\n\n/**\n * An execution identifier representing a node's position in nested subgraphs.\n * Also known as ExecutionId in some contexts.\n *\n * Format: Colon-separated path of node IDs\n * Example: \"123:456:789\" (node 789 in subgraph 456 in subgraph 123)\n */\nexport type NodeExecutionId = string\n\n/**\n * Type guard to check if a value is a NodeLocatorId\n */\nexport function isNodeLocatorId(value: unknown): value is NodeLocatorId {\n if (typeof value !== 'string') return false\n\n // Check if it's a simple node ID (root graph node)\n const parts = value.split(':')\n if (parts.length === 1) {\n // Simple node ID - must be non-empty\n return value.length > 0\n }\n\n // Check for UUID:nodeId format\n if (parts.length !== 2) return false\n\n // Check that node ID part is not empty\n if (!parts[1]) return false\n\n // Basic UUID format check (8-4-4-4-12 hex characters)\n const uuidPattern =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n return uuidPattern.test(parts[0])\n}\n\n/**\n * Type guard to check if a value is a NodeExecutionId\n */\nexport function isNodeExecutionId(value: unknown): value is NodeExecutionId {\n if (typeof value !== 'string') return false\n // Must contain at least one colon to be an execution ID\n return value.includes(':')\n}\n\n/**\n * Parse a NodeLocatorId into its components\n * @param id The NodeLocatorId to parse\n * @returns The subgraph UUID and local node ID, or null if invalid\n */\nexport function parseNodeLocatorId(\n id: string\n): { subgraphUuid: string | null; localNodeId: NodeId } | null {\n if (!isNodeLocatorId(id)) return null\n\n const parts = id.split(':')\n\n if (parts.length === 1) {\n // Simple node ID (root graph)\n return {\n subgraphUuid: null,\n localNodeId: isNaN(Number(id)) ? id : Number(id)\n }\n }\n\n const [subgraphUuid, localNodeId] = parts\n return {\n subgraphUuid,\n localNodeId: isNaN(Number(localNodeId)) ? localNodeId : Number(localNodeId)\n }\n}\n\n/**\n * Create a NodeLocatorId from components\n * @param subgraphUuid The UUID of the immediate containing subgraph\n * @param localNodeId The local node ID within that subgraph\n * @returns A properly formatted NodeLocatorId\n */\nexport function createNodeLocatorId(\n subgraphUuid: string,\n localNodeId: NodeId\n): NodeLocatorId {\n return `${subgraphUuid}:${localNodeId}`\n}\n\n/**\n * Parse a NodeExecutionId into its component node IDs\n * @param id The NodeExecutionId to parse\n * @returns Array of node IDs from root to target, or null if not an execution ID\n */\nexport function parseNodeExecutionId(id: string): NodeId[] | null {\n if (!isNodeExecutionId(id)) return null\n\n return id\n .split(':')\n .map((part) => (isNaN(Number(part)) ? part : Number(part)))\n}\n\n/**\n * Create a NodeExecutionId from an array of node IDs\n * @param nodeIds Array of node IDs from root to target\n * @returns A properly formatted NodeExecutionId\n */\nexport function createNodeExecutionId(nodeIds: NodeId[]): NodeExecutionId {\n return nodeIds.join(':')\n}\n","import type {\n INodeSlot,\n LGraph,\n LGraphNode,\n Subgraph\n} from '@/lib/litegraph/src/litegraph'\nimport type { ResultItemType } from '@/schemas/apiSchema'\n\n/**\n * Check if an error is an AbortError triggered by `AbortController#abort`\n * when cancelling a request.\n */\nexport const isAbortError = (\n err: unknown\n): err is DOMException & { name: 'AbortError' } =>\n err instanceof DOMException && err.name === 'AbortError'\n\nexport const isSubgraph = (\n item: LGraph | Subgraph | undefined | null\n): item is Subgraph => item?.isRootGraph === false\n\n/**\n * Check if an item is non-nullish.\n */\nexport const isNonNullish = <T>(item: T | undefined | null): item is T =>\n item != null\n\n/**\n * Type guard to check if a node is a subgraph input/output node.\n * These nodes are essential to subgraph structure and should not be removed.\n */\nexport const isSubgraphIoNode = (\n node: LGraphNode\n): node is LGraphNode & {\n constructor: { comfyClass: 'SubgraphInputNode' | 'SubgraphOutputNode' }\n} => {\n const nodeClass = node.constructor?.comfyClass\n return nodeClass === 'SubgraphInputNode' || nodeClass === 'SubgraphOutputNode'\n}\n\n/**\n * Type guard for slot objects (inputs/outputs)\n */\nexport const isSlotObject = (obj: unknown): obj is INodeSlot => {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n 'name' in obj &&\n 'type' in obj &&\n 'boundingRect' in obj\n )\n}\n\n/**\n * Type guard to check if a string is a valid ResultItemType\n * ResultItemType is used for asset categorization (input/output/temp)\n */\nexport const isResultItemType = (\n value: string | undefined\n): value is ResultItemType => {\n return value === 'input' || value === 'output' || value === 'temp'\n}\n","import type {\n LGraph,\n LGraphNode,\n Subgraph\n} from '@/lib/litegraph/src/litegraph'\nimport type { NodeExecutionId, NodeLocatorId } from '@/types/nodeIdentification'\nimport { parseNodeLocatorId } from '@/types/nodeIdentification'\n\nimport { isSubgraphIoNode } from './typeGuardUtil'\n\ninterface NodeWithId {\n id: string | number\n subgraphId?: string | null\n}\n\n/**\n * Constructs a locator ID from node data with optional subgraph context.\n *\n * @param nodeData - Node data containing id and optional subgraphId\n * @returns The locator ID string\n */\nexport function getLocatorIdFromNodeData(nodeData: NodeWithId): string {\n return nodeData.subgraphId\n ? `${nodeData.subgraphId}:${String(nodeData.id)}`\n : String(nodeData.id)\n}\n\n/**\n * Parses an execution ID into its component parts.\n *\n * @param executionId - The execution ID (e.g., \"123:456:789\" or \"789\")\n * @returns Array of node IDs in the path, or null if invalid\n */\nexport function parseExecutionId(executionId: string): string[] | null {\n if (!executionId || typeof executionId !== 'string') return null\n return executionId.split(':').filter((part) => part.length > 0)\n}\n\n/**\n * Extracts the local node ID from an execution ID.\n *\n * @param executionId - The execution ID (e.g., \"123:456:789\" or \"789\")\n * @returns The local node ID or null if invalid\n */\nexport function getLocalNodeIdFromExecutionId(\n executionId: string\n): string | null {\n const parts = parseExecutionId(executionId)\n return parts ? parts[parts.length - 1] : null\n}\n\n/**\n * Extracts the subgraph path from an execution ID.\n *\n * @param executionId - The execution ID (e.g., \"123:456:789\" or \"789\")\n * @returns Array of subgraph node IDs (excluding the final node ID), or empty array\n */\nexport function getSubgraphPathFromExecutionId(executionId: string): string[] {\n const parts = parseExecutionId(executionId)\n return parts ? parts.slice(0, -1) : []\n}\n\n/**\n * Visits each node in a graph (non-recursive, single level).\n *\n * @param graph - The graph to visit nodes from\n * @param visitor - Function called for each node\n */\nexport function visitGraphNodes(\n graph: LGraph | Subgraph,\n visitor: (node: LGraphNode) => void\n): void {\n for (const node of graph.nodes) {\n visitor(node)\n }\n}\n\n/**\n * Traverses a path of subgraphs to reach a target graph.\n *\n * @param startGraph - The graph to start from\n * @param path - Array of subgraph node IDs to traverse\n * @returns The target graph or null if path is invalid\n */\nexport function traverseSubgraphPath(\n startGraph: LGraph | Subgraph,\n path: string[]\n): LGraph | Subgraph | null {\n let currentGraph: LGraph | Subgraph = startGraph\n\n for (const nodeId of path) {\n const node = currentGraph.getNodeById(nodeId)\n if (!node?.isSubgraphNode?.() || !node.subgraph) return null\n currentGraph = node.subgraph\n }\n\n return currentGraph\n}\n\n/**\n * Traverses all nodes in a graph hierarchy (including subgraphs) and invokes\n * a callback on each node that has the specified property.\n *\n * @param graph - The root graph to start traversal from\n * @param callbackProperty - The name of the callback property to invoke on each node\n */\nexport function triggerCallbackOnAllNodes(\n graph: LGraph | Subgraph,\n callbackProperty: keyof LGraphNode\n): void {\n forEachNode(graph, (node) => {\n const callback = node[callbackProperty]\n if (typeof callback === 'function') {\n callback.call(node)\n }\n })\n}\n\n/**\n * Maps a function over all nodes in a graph hierarchy (including subgraphs).\n * This is a pure functional traversal that doesn't mutate the graph.\n *\n * @param graph - The root graph to traverse\n * @param mapFn - Function to apply to each node\n * @returns Array of mapped results (excluding undefined values)\n */\nexport function mapAllNodes<T>(\n graph: LGraph | Subgraph,\n mapFn: (node: LGraphNode) => T | undefined\n): T[] {\n const results: T[] = []\n\n visitGraphNodes(graph, (node) => {\n // Recursively map over subgraphs first\n if (node.isSubgraphNode?.() && node.subgraph) {\n results.push(...mapAllNodes(node.subgraph, mapFn))\n }\n\n // Apply map function to current node\n const result = mapFn(node)\n if (result !== undefined) {\n results.push(result)\n }\n })\n\n return results\n}\n\n/**\n * Executes a side-effect function on all nodes in a graph hierarchy.\n * This is for operations that modify nodes or perform side effects.\n *\n * @param graph - The root graph to traverse\n * @param fn - Function to execute on each node\n */\nexport function forEachNode(\n graph: LGraph | Subgraph,\n fn: (node: LGraphNode) => void\n): void {\n visitGraphNodes(graph, (node) => {\n // Recursively process subgraphs first\n if (node.isSubgraphNode?.() && node.subgraph) {\n forEachNode(node.subgraph, fn)\n }\n\n // Execute function on current node\n fn(node)\n })\n}\n\n/**\n * Collects all nodes in a graph hierarchy (including subgraphs) into a flat array.\n *\n * @param graph - The root graph to collect nodes from\n * @param filter - Optional filter function to include only specific nodes\n * @returns Array of all nodes in the graph hierarchy\n */\nexport function collectAllNodes(\n graph: LGraph | Subgraph,\n filter?: (node: LGraphNode) => boolean\n): LGraphNode[] {\n return mapAllNodes(graph, (node) => {\n if (!filter || filter(node)) {\n return node\n }\n return undefined\n })\n}\n\n/**\n * Finds a node by ID anywhere in the graph hierarchy.\n *\n * @param graph - The root graph to search\n * @param nodeId - The ID of the node to find\n * @returns The node if found, null otherwise\n */\nexport function findNodeInHierarchy(\n graph: LGraph | Subgraph,\n nodeId: string | number\n): LGraphNode | null {\n // Check current graph\n const node = graph.getNodeById(nodeId)\n if (node) return node\n\n // Search in subgraphs\n for (const node of graph.nodes) {\n if (node.isSubgraphNode?.() && node.subgraph) {\n const found = findNodeInHierarchy(node.subgraph, nodeId)\n if (found) return found\n }\n }\n\n return null\n}\n\n/**\n * Find a subgraph by its UUID anywhere in the graph hierarchy.\n *\n * @param graph - The root graph to search\n * @param targetUuid - The UUID of the subgraph to find\n * @returns The subgraph if found, null otherwise\n */\nexport function findSubgraphByUuid(\n graph: LGraph | Subgraph,\n targetUuid: string\n): Subgraph | null {\n // Check all nodes in the current graph\n for (const node of graph.nodes) {\n if (node.isSubgraphNode?.() && node.subgraph) {\n if (node.subgraph.id === targetUuid) {\n return node.subgraph\n }\n // Recursively search in nested subgraphs\n const found = findSubgraphByUuid(node.subgraph, targetUuid)\n if (found) return found\n }\n }\n return null\n}\n\n/**\n * Iteratively finds the path of subgraph IDs to a target subgraph.\n * @param rootGraph The graph to start searching from.\n * @param targetId The ID of the subgraph to find.\n * @returns An array of subgraph IDs representing the path, or `null` if not found.\n */\nexport function findSubgraphPathById(\n rootGraph: LGraph,\n targetId: string\n): string[] | null {\n const stack: { graph: LGraph | Subgraph; path: string[] }[] = [\n { graph: rootGraph, path: [] }\n ]\n\n while (stack.length > 0) {\n const { graph, path } = stack.pop()!\n\n // Check if graph exists and has _nodes property\n if (!graph || !graph._nodes || !Array.isArray(graph._nodes)) {\n continue\n }\n\n for (const node of graph._nodes) {\n if (node.isSubgraphNode?.() && node.subgraph) {\n const newPath = [...path, String(node.subgraph.id)]\n if (node.subgraph.id === targetId) {\n return newPath\n }\n stack.push({ graph: node.subgraph, path: newPath })\n }\n }\n }\n\n return null\n}\n\n/**\n * Get a node by its execution ID from anywhere in the graph hierarchy.\n * Execution IDs use hierarchical format like \"123:456:789\" for nested nodes.\n *\n * @param rootGraph - The root graph to search from\n * @param executionId - The execution ID (e.g., \"123:456:789\" or \"789\")\n * @returns The node if found, null otherwise\n */\nexport function getNodeByExecutionId(\n rootGraph: LGraph,\n executionId: string\n): LGraphNode | null {\n if (!rootGraph) return null\n\n const localNodeId = getLocalNodeIdFromExecutionId(executionId)\n if (!localNodeId) return null\n\n const subgraphPath = getSubgraphPathFromExecutionId(executionId)\n\n // If no subgraph path, it's in the root graph\n if (subgraphPath.length === 0) {\n return rootGraph.getNodeById(localNodeId) || null\n }\n\n // Traverse to the target subgraph\n const targetGraph = traverseSubgraphPath(rootGraph, subgraphPath)\n if (!targetGraph) return null\n\n // Get the node from the target graph\n return targetGraph.getNodeById(localNodeId) || null\n}\n\n/**\n * Get a node by its locator ID from anywhere in the graph hierarchy.\n * Locator IDs use UUID format like \"uuid:nodeId\" for subgraph nodes.\n *\n * @param rootGraph - The root graph to search from\n * @param locatorId - The locator ID (e.g., \"uuid:123\" or \"123\")\n * @returns The node if found, null otherwise\n */\nexport function getNodeByLocatorId(\n rootGraph: LGraph,\n locatorId: NodeLocatorId | string\n): LGraphNode | null {\n if (!rootGraph) return null\n\n const parsedIds = parseNodeLocatorId(locatorId)\n if (!parsedIds) return null\n\n const { subgraphUuid, localNodeId } = parsedIds\n\n // If no subgraph UUID, it's in the root graph\n if (!subgraphUuid) {\n return rootGraph.getNodeById(localNodeId) || null\n }\n\n // Find the subgraph with the matching UUID\n const targetSubgraph = findSubgraphByUuid(rootGraph, subgraphUuid)\n if (!targetSubgraph) return null\n\n return targetSubgraph.getNodeById(localNodeId) || null\n}\n\n/**\n * Finds the root graph from any graph in the hierarchy.\n *\n * @param graph - Any graph or subgraph in the hierarchy\n * @returns The root graph\n */\nexport function getRootGraph(graph: LGraph | Subgraph): LGraph | Subgraph {\n let current: LGraph | Subgraph = graph\n while ('rootGraph' in current && current.rootGraph) {\n current = current.rootGraph\n }\n return current\n}\n\n/**\n * Applies a function to all nodes whose type matches a subgraph ID.\n * Operates on the entire graph hierarchy starting from the root.\n *\n * @param rootGraph - The root graph to search in\n * @param subgraphId - The ID/type of the subgraph to match nodes against\n * @param fn - Function to apply to each matching node\n */\nexport function forEachSubgraphNode(\n rootGraph: LGraph | Subgraph | null | undefined,\n subgraphId: string | null | undefined,\n fn: (node: LGraphNode) => void\n): void {\n if (!rootGraph || !subgraphId) return\n\n forEachNode(rootGraph, (node) => {\n if (node.type === subgraphId) {\n fn(node)\n }\n })\n}\n\n/**\n * Maps a function over all nodes whose type matches a subgraph ID.\n * Operates on the entire graph hierarchy starting from the root.\n *\n * @param rootGraph - The root graph to search in\n * @param subgraphId - The ID/type of the subgraph to match nodes against\n * @param mapFn - Function to apply to each matching node\n * @returns Array of mapped results\n */\nexport function mapSubgraphNodes<T>(\n rootGraph: LGraph | Subgraph | null | undefined,\n subgraphId: string | null | undefined,\n mapFn: (node: LGraphNode) => T\n): T[] {\n if (!rootGraph || !subgraphId) return []\n\n return mapAllNodes(rootGraph, (node) => {\n if (node.type === subgraphId) {\n return mapFn(node)\n }\n return undefined\n })\n}\n\n/**\n * Gets all non-IO nodes from a subgraph (excludes SubgraphInputNode and SubgraphOutputNode).\n * These are the user-created nodes that can be safely removed when clearing a subgraph.\n *\n * @param subgraph - The subgraph to get non-IO nodes from\n * @returns Array of non-IO nodes (user-created nodes)\n */\nexport function getAllNonIoNodesInSubgraph(subgraph: Subgraph): LGraphNode[] {\n return subgraph.nodes.filter((node) => !isSubgraphIoNode(node))\n}\n\n/**\n * Options for traverseNodesDepthFirst function\n */\ninterface TraverseNodesOptions<T> {\n /** Function called for each node during traversal */\n visitor?: (node: LGraphNode, context: T) => T\n /** Initial context value */\n initialContext?: T\n /** Whether to traverse into subgraph nodes (default: true) */\n expandSubgraphs?: boolean\n}\n\n/**\n * Performs depth-first traversal of nodes and their subgraphs.\n * Generic visitor pattern that can be used for various node processing tasks.\n *\n * @param nodes - Starting nodes for traversal\n * @param options - Optional traversal configuration\n */\nexport function traverseNodesDepthFirst<T = void>(\n nodes: LGraphNode[],\n options?: TraverseNodesOptions<T>\n): void {\n const {\n visitor = () => undefined as T,\n initialContext = undefined as T,\n expandSubgraphs = true\n } = options || {}\n type StackItem = { node: LGraphNode; context: T }\n const stack: StackItem[] = []\n\n // Initialize stack with starting nodes\n for (const node of nodes) {\n stack.push({ node, context: initialContext })\n }\n\n // Process stack iteratively (DFS)\n while (stack.length > 0) {\n const { node, context } = stack.pop()!\n\n // Visit node and get updated context for children\n const childContext = visitor(node, context)\n\n // If it's a subgraph and we should expand, add children to stack\n if (expandSubgraphs && node.isSubgraphNode?.() && node.subgraph) {\n // Process children in reverse order to maintain left-to-right DFS processing\n // when popping from stack (LIFO). Iterate backwards to avoid array reversal.\n const children = node.subgraph.nodes\n for (let i = children.length - 1; i >= 0; i--) {\n stack.push({ node: children[i], context: childContext })\n }\n }\n }\n}\n\n/**\n * Reduces all nodes in a graph hierarchy to a single value using a reducer function.\n * Single-pass traversal for efficient aggregation.\n *\n * @param graph - The root graph to traverse\n * @param reducer - Function that reduces each node into the accumulator\n * @param initialValue - The initial accumulator value\n * @returns The final reduced value\n */\nexport function reduceAllNodes<T>(\n graph: LGraph | Subgraph,\n reducer: (accumulator: T, node: LGraphNode) => T,\n initialValue: T\n): T {\n let result = initialValue\n forEachNode(graph, (node) => {\n result = reducer(result, node)\n })\n return result\n}\n\n/**\n * Options for collectFromNodes function\n */\ninterface CollectFromNodesOptions<T, C> {\n /** Function that returns data to collect for each node */\n collector?: (node: LGraphNode, context: C) => T | null\n /** Function that builds context for child nodes */\n contextBuilder?: (node: LGraphNode, parentContext: C) => C\n /** Initial context value */\n initialContext?: C\n /** Whether to traverse into subgraph nodes (default: true) */\n expandSubgraphs?: boolean\n}\n\n/**\n * Collects nodes with custom data during depth-first traversal.\n * Generic collector that can gather any type of data per node.\n *\n * @param nodes - Starting nodes for traversal\n * @param options - Optional collection configuration\n * @returns Array of collected data\n */\nexport function collectFromNodes<T = LGraphNode, C = void>(\n nodes: LGraphNode[],\n options?: CollectFromNodesOptions<T, C>\n): T[] {\n const {\n collector = (node: LGraphNode) => node as unknown as T,\n contextBuilder = () => undefined as C,\n initialContext = undefined as C,\n expandSubgraphs = true\n } = options || {}\n const results: T[] = []\n\n traverseNodesDepthFirst(nodes, {\n visitor: (node, context) => {\n const data = collector(node, context)\n if (data !== null) {\n results.push(data)\n }\n return contextBuilder(node, context)\n },\n initialContext,\n expandSubgraphs\n })\n\n return results\n}\n\n/**\n * Collects execution IDs for selected nodes and all their descendants.\n * Uses the generic DFS traversal with optimized string building.\n *\n * @param selectedNodes - The selected nodes to process\n * @returns Array of execution IDs for selected nodes and all nodes within selected subgraphs\n */\nexport function getExecutionIdsForSelectedNodes(\n selectedNodes: LGraphNode[],\n startGraph = selectedNodes[0]?.graph\n): NodeExecutionId[] {\n if (!startGraph) return []\n const rootGraph = startGraph.rootGraph\n const parentPath = startGraph.isRootGraph\n ? ''\n : findPartialExecutionPathToGraph(startGraph, rootGraph)\n if (parentPath === undefined) return []\n\n return collectFromNodes<NodeExecutionId, string>(selectedNodes, {\n collector: (node, parentExecutionId) => {\n const nodeId = String(node.id)\n return parentExecutionId ? `${parentExecutionId}:${nodeId}` : nodeId\n },\n contextBuilder: (node, parentExecutionId) => {\n const nodeId = String(node.id)\n return parentExecutionId ? `${parentExecutionId}:${nodeId}` : nodeId\n },\n initialContext: parentPath,\n expandSubgraphs: true\n })\n}\n\nfunction findPartialExecutionPathToGraph(\n target: LGraph,\n root: LGraph\n): string | undefined {\n for (const node of root.nodes) {\n if (!node.isSubgraphNode()) continue\n\n if (node.subgraph === target) return `${node.id}`\n\n const subpath = findPartialExecutionPathToGraph(target, node.subgraph)\n if (subpath !== undefined) return node.id + ':' + subpath\n }\n return undefined\n}\n","import type { CompassCorners } from './interfaces'\nimport { dist2 } from './measure'\nimport type { CanvasPointerEvent } from './types/events'\n\n/**\n * Allows click and drag actions to be declared ahead of time during a pointerdown event.\n *\n * By default, it retains the most recent event of each type until it is reset (on pointerup).\n * - {@link eDown}\n * - {@link eMove}\n * - {@link eUp}\n *\n * Depending on whether the user clicks or drags the pointer, only the appropriate callbacks are called:\n * - {@link onClick}\n * - {@link onDoubleClick}\n * - {@link onDragStart}\n * - {@link onDrag}\n * - {@link onDragEnd}\n * - {@link finally}\n * @see\n * - {@link LGraphCanvas.processMouseDown}\n * - {@link LGraphCanvas.processMouseMove}\n * - {@link LGraphCanvas.processMouseUp}\n */\nexport class CanvasPointer {\n /** Maximum time in milliseconds to ignore click drift */\n static bufferTime = 150\n\n /** Maximum gap between pointerup and pointerdown events to be considered as a double click */\n static doubleClickTime = 300\n\n /** Maximum offset from click location */\n static get maxClickDrift() {\n return this.#maxClickDrift\n }\n\n static set maxClickDrift(value) {\n this.#maxClickDrift = value\n this.#maxClickDrift2 = value * value\n }\n\n static #maxClickDrift = 6\n /** {@link maxClickDrift} squared. Used to calculate click drift without `sqrt`. */\n static #maxClickDrift2 = this.#maxClickDrift ** 2\n\n /** Assume that \"wheel\" events with both deltaX and deltaY less than this value are trackpad gestures. */\n static trackpadThreshold = 60\n\n /**\n * The minimum time between \"wheel\" events to allow switching between trackpad\n * and mouse modes.\n *\n * This prevents trackpad \"flick\" panning from registering as regular mouse wheel.\n * After a flick gesture is complete, the automatic wheel events are sent with\n * reduced frequency, but much higher deltaX and deltaY values.\n */\n static trackpadMaxGap = 500\n\n /** The maximum time in milliseconds to buffer a high-res wheel event. */\n static maxHighResBufferTime = 10\n\n /** The element this PointerState should capture input against when dragging. */\n element: Element\n /** Pointer ID used by drag capture. */\n pointerId?: number\n\n /** Set to true when if the pointer moves far enough after a down event, before the corresponding up event is fired. */\n dragStarted: boolean = false\n\n /** The {@link eUp} from the last successful click */\n eLastDown?: CanvasPointerEvent\n\n /** Used downstream for touch event support. */\n isDouble: boolean = false\n /** Used downstream for touch event support. */\n isDown: boolean = false\n\n /** The resize handle currently being hovered or dragged */\n resizeDirection?: CompassCorners\n\n /**\n * If `true`, {@link eDown}, {@link eMove}, and {@link eUp} will be set to\n * `undefined` when {@link reset} is called.\n *\n * Default: `true`\n */\n clearEventsOnReset: boolean = true\n\n /** The last pointerdown event for the primary button */\n eDown?: CanvasPointerEvent\n /** The last pointermove event for the primary button */\n eMove?: CanvasPointerEvent\n /** The last pointerup event for the primary button */\n eUp?: CanvasPointerEvent\n\n /** Currently detected input device type */\n detectedDevice: 'mouse' | 'trackpad' = 'mouse'\n\n /** Timestamp of last wheel event for cooldown tracking */\n lastWheelEventTime: number = 0\n\n /** Flag to track if we've received the first wheel event */\n hasReceivedWheelEvent: boolean = false\n\n /** Buffered Linux wheel event awaiting confirmation */\n bufferedLinuxEvent?: WheelEvent\n\n /** Timestamp when Linux event was buffered */\n bufferedLinuxEventTime: number = 0\n\n /** Timer ID for Linux buffer clearing */\n linuxBufferTimeoutId?: ReturnType<typeof setTimeout>\n\n /**\n * If set, as soon as the mouse moves outside the click drift threshold, this action is run once.\n * @param pointer [DEPRECATED] This parameter will be removed in a future release.\n * @param eMove The pointermove event of this ongoing drag action.\n *\n * It is possible for no `pointermove` events to occur, but still be far from\n * the original `pointerdown` event. In this case, {@link eMove} will be null, and\n * {@link onDragEnd} will be called immediately after {@link onDragStart}.\n */\n onDragStart?(pointer: this, eMove?: CanvasPointerEvent): unknown\n\n /**\n * Called on pointermove whilst dragging.\n * @param eMove The pointermove event of this ongoing drag action\n */\n onDrag?(eMove: CanvasPointerEvent): unknown\n\n /**\n * Called on pointerup after dragging (i.e. not called if clicked).\n * @param upEvent The pointerup or pointermove event that triggered this callback\n */\n onDragEnd?(upEvent: CanvasPointerEvent): unknown\n\n /**\n * Callback that will be run once, the next time a pointerup event appears to be a normal click.\n * @param upEvent The pointerup or pointermove event that triggered this callback\n */\n onClick?(upEvent: CanvasPointerEvent): unknown\n\n /**\n * Callback that will be run once, the next time a pointerup event appears to be a normal click.\n * @param upEvent The pointerup or pointermove event that triggered this callback\n */\n onDoubleClick?(upEvent: CanvasPointerEvent): unknown\n\n /**\n * Run-once callback, called at the end of any click or drag, whether or not it was successful in any way.\n *\n * The setter of this callback will call the existing value before replacing it.\n * Therefore, simply setting this value twice will execute the first callback.\n */\n get finally() {\n return this.#finally\n }\n\n set finally(value) {\n try {\n this.#finally?.()\n } finally {\n this.#finally = value\n }\n }\n\n #finally?: () => unknown\n\n constructor(element: Element) {\n this.element = element\n }\n\n /**\n * Callback for `pointerdown` events. To be used as the event handler (or called by it).\n * @param e The `pointerdown` event\n */\n down(e: CanvasPointerEvent): void {\n this.reset()\n this.eDown = e\n this.pointerId = e.pointerId\n this.element.setPointerCapture(e.pointerId)\n }\n\n /**\n * Callback for `pointermove` events. To be used as the event handler (or called by it).\n * @param e The `pointermove` event\n */\n move(e: CanvasPointerEvent): void {\n const { eDown } = this\n if (!eDown) return\n\n // No buttons down, but eDown exists - clean up & leave\n if (!e.buttons) {\n this.reset()\n return\n }\n\n // Primary button released - treat as pointerup.\n if (!(e.buttons & eDown.buttons)) {\n this.#completeClick(e)\n this.reset()\n return\n }\n this.eMove = e\n this.onDrag?.(e)\n\n // Dragging, but no callback to run\n if (this.dragStarted) return\n\n const longerThanBufferTime =\n e.timeStamp - eDown.timeStamp > CanvasPointer.bufferTime\n if (longerThanBufferTime || !this.#hasSamePosition(e, eDown)) {\n this.#setDragStarted(e)\n }\n }\n\n /**\n * Callback for `pointerup` events. To be used as the event handler (or called by it).\n * @param e The `pointerup` event\n */\n up(e: CanvasPointerEvent): boolean {\n if (e.button !== this.eDown?.button) return false\n\n this.#completeClick(e)\n const { dragStarted } = this\n this.reset()\n return !dragStarted\n }\n\n #completeClick(e: CanvasPointerEvent): void {\n const { eDown } = this\n if (!eDown) return\n\n this.eUp = e\n\n if (this.dragStarted) {\n // A move event already started drag\n this.onDragEnd?.(e)\n } else if (!this.#hasSamePosition(e, eDown)) {\n // Teleport without a move event (e.g. tab out, move, tab back)\n this.#setDragStarted()\n this.onDragEnd?.(e)\n } else if (this.onDoubleClick && this.#isDoubleClick()) {\n // Double-click event\n this.onDoubleClick(e)\n this.eLastDown = undefined\n } else {\n // Normal click event\n this.onClick?.(e)\n this.eLastDown = eDown\n }\n }\n\n /**\n * Checks if two events occurred near each other - not further apart than the maximum click drift.\n * @param a The first event to compare\n * @param b The second event to compare\n * @param tolerance2 The maximum distance (squared) before the positions are considered different\n * @returns `true` if the two events were no more than {@link maxClickDrift} apart, otherwise `false`\n */\n #hasSamePosition(\n a: PointerEvent,\n b: PointerEvent,\n tolerance2 = CanvasPointer.#maxClickDrift2\n ): boolean {\n const drift = dist2(a.clientX, a.clientY, b.clientX, b.clientY)\n return drift <= tolerance2\n }\n\n /**\n * Checks whether the pointer is currently past the max click drift threshold.\n * @returns `true` if the latest pointer event is past the the click drift threshold\n */\n #isDoubleClick(): boolean {\n const { eDown, eLastDown } = this\n if (!eDown || !eLastDown) return false\n\n // Use thrice the drift distance for double-click gap\n const tolerance2 = (3 * CanvasPointer.#maxClickDrift) ** 2\n const diff = eDown.timeStamp - eLastDown.timeStamp\n return (\n diff > 0 &&\n diff < CanvasPointer.doubleClickTime &&\n this.#hasSamePosition(eDown, eLastDown, tolerance2)\n )\n }\n\n #setDragStarted(eMove?: CanvasPointerEvent): void {\n this.dragStarted = true\n this.onDragStart?.(this, eMove)\n delete this.onDragStart\n }\n\n /**\n * Checks if the given wheel event is part of a trackpad gesture.\n * This method now uses the new device detection internally for improved accuracy.\n * @param e The wheel event to check\n * @returns `true` if the event is part of a trackpad gesture, otherwise `false`\n */\n isTrackpadGesture(e: WheelEvent): boolean {\n // Use the new device detection\n const now = performance.now()\n const timeSinceLastEvent = Math.max(0, now - this.lastWheelEventTime)\n this.lastWheelEventTime = now\n\n if (this.#isHighResWheelEvent(e, now)) {\n this.detectedDevice = 'mouse'\n } else if (this.#isWithinCooldown(timeSinceLastEvent)) {\n if (this.#shouldBufferLinuxEvent(e)) {\n this.#bufferLinuxEvent(e, now)\n }\n } else {\n this.#updateDeviceMode(e, now)\n this.hasReceivedWheelEvent = true\n }\n\n return this.detectedDevice === 'trackpad'\n }\n\n /**\n * Validates buffered high res wheel events and switches to mouse mode if pattern matches.\n * @returns `true` if switched to mouse mode\n */\n #isHighResWheelEvent(event: WheelEvent, now: number): boolean {\n if (!this.bufferedLinuxEvent || this.bufferedLinuxEventTime <= 0) {\n return false\n }\n\n const timeSinceBuffer = now - this.bufferedLinuxEventTime\n\n if (timeSinceBuffer > CanvasPointer.maxHighResBufferTime) {\n this.#clearLinuxBuffer()\n return false\n }\n\n if (\n event.deltaX === 0 &&\n this.#isLinuxWheelPattern(this.bufferedLinuxEvent.deltaY, event.deltaY)\n ) {\n this.#clearLinuxBuffer()\n return true\n }\n\n return false\n }\n\n /**\n * Checks if we're within the cooldown period where mode switching is disabled.\n */\n #isWithinCooldown(timeSinceLastEvent: number): boolean {\n const isFirstEvent = !this.hasReceivedWheelEvent\n const cooldownExpired = timeSinceLastEvent >= CanvasPointer.trackpadMaxGap\n return !isFirstEvent && !cooldownExpired\n }\n\n /**\n * Updates the device mode based on event patterns.\n */\n #updateDeviceMode(event: WheelEvent, now: number): void {\n if (this.#isTrackpadPattern(event)) {\n this.detectedDevice = 'trackpad'\n } else if (this.#isMousePattern(event)) {\n this.detectedDevice = 'mouse'\n } else if (\n this.detectedDevice === 'trackpad' &&\n this.#shouldBufferLinuxEvent(event)\n ) {\n this.#bufferLinuxEvent(event, now)\n }\n }\n\n /**\n * Clears the buffered Linux wheel event and associated timer.\n */\n #clearLinuxBuffer(): void {\n this.bufferedLinuxEvent = undefined\n this.bufferedLinuxEventTime = 0\n if (this.linuxBufferTimeoutId !== undefined) {\n clearTimeout(this.linuxBufferTimeoutId)\n this.linuxBufferTimeoutId = undefined\n }\n }\n\n /**\n * Checks if the event matches trackpad input patterns.\n * @param event The wheel event to check\n */\n #isTrackpadPattern(event: WheelEvent): boolean {\n // Two-finger panning: non-zero deltaX AND deltaY\n if (event.deltaX !== 0 && event.deltaY !== 0) return true\n\n // Pinch-to-zoom: ctrlKey with small deltaY\n if (event.ctrlKey && Math.abs(event.deltaY) < 10) return true\n\n return false\n }\n\n /**\n * Checks if the event matches mouse wheel input patterns.\n * @param event The wheel event to check\n */\n #isMousePattern(event: WheelEvent): boolean {\n const absoluteDeltaY = Math.abs(event.deltaY)\n\n // Primary threshold for switching from trackpad to mouse\n if (absoluteDeltaY > 80) return true\n\n // Secondary threshold when already in mouse mode\n return (\n absoluteDeltaY >= 60 &&\n event.deltaX === 0 &&\n this.detectedDevice === 'mouse'\n )\n }\n\n /**\n * Checks if the event should be buffered as a potential Linux wheel event.\n * @param event The wheel event to check\n */\n #shouldBufferLinuxEvent(event: WheelEvent): boolean {\n const absoluteDeltaY = Math.abs(event.deltaY)\n const isInLinuxRange = absoluteDeltaY >= 10 && absoluteDeltaY < 60\n const isVerticalOnly = event.deltaX === 0\n const hasIntegerDelta = Number.isInteger(event.deltaY)\n\n return (\n this.detectedDevice === 'trackpad' &&\n isInLinuxRange &&\n isVerticalOnly &&\n hasIntegerDelta\n )\n }\n\n /**\n * Buffers a potential Linux wheel event for later confirmation.\n * @param event The event to buffer\n * @param now The current timestamp\n */\n #bufferLinuxEvent(event: WheelEvent, now: number): void {\n if (this.linuxBufferTimeoutId !== undefined) {\n clearTimeout(this.linuxBufferTimeoutId)\n }\n\n this.bufferedLinuxEvent = event\n this.bufferedLinuxEventTime = now\n\n // Set timeout to clear buffer after 10ms\n this.linuxBufferTimeoutId = setTimeout(() => {\n this.#clearLinuxBuffer()\n }, CanvasPointer.maxHighResBufferTime)\n }\n\n /**\n * Checks if two deltaY values follow a Linux wheel pattern (divisibility).\n * @param deltaY1 The first deltaY value\n * @param deltaY2 The second deltaY value\n */\n #isLinuxWheelPattern(deltaY1: number, deltaY2: number): boolean {\n const absolute1 = Math.abs(deltaY1)\n const absolute2 = Math.abs(deltaY2)\n\n if (absolute1 === 0 || absolute2 === 0) return false\n if (absolute1 === absolute2) return true\n\n // Check if one value is a multiple of the other\n return absolute1 % absolute2 === 0 || absolute2 % absolute1 === 0\n }\n\n /**\n * Resets the state of this {@link CanvasPointer} instance.\n *\n * The {@link finally} callback is first executed, then all callbacks and intra-click\n * state is cleared.\n */\n reset(): void {\n // The setter executes the callback before clearing it\n this.finally = undefined\n delete this.onClick\n delete this.onDoubleClick\n delete this.onDragStart\n delete this.onDrag\n delete this.onDragEnd\n\n this.isDown = false\n this.isDouble = false\n this.dragStarted = false\n this.resizeDirection = undefined\n\n if (this.clearEventsOnReset) {\n this.eDown = undefined\n this.eMove = undefined\n this.eUp = undefined\n }\n\n const { element, pointerId } = this\n this.pointerId = undefined\n if (typeof pointerId === 'number' && element.hasPointerCapture(pointerId)) {\n element.releasePointerCapture(pointerId)\n }\n }\n}\n","export class NullGraphError extends Error {\n constructor(\n message: string = 'Attempted to access LGraph reference that was null or undefined.',\n cause?: Error\n ) {\n super(message, { cause })\n this.name = 'NullGraphError'\n }\n}\n","import type { LGraphNode } from './LGraphNode'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n/**\n * Default properties to track\n */\nconst DEFAULT_TRACKED_PROPERTIES: string[] = [\n 'title',\n 'flags.collapsed',\n 'flags.pinned',\n 'mode',\n 'color',\n 'bgcolor'\n]\n/**\n * Manages node properties with optional change tracking and instrumentation.\n */\nexport class LGraphNodeProperties {\n /** The node this property manager belongs to */\n node: LGraphNode\n\n /** Set of property paths that have been instrumented */\n #instrumentedPaths = new Set<string>()\n\n constructor(node: LGraphNode) {\n this.node = node\n\n this.#setupInstrumentation()\n }\n\n /**\n * Sets up property instrumentation for all tracked properties\n */\n #setupInstrumentation(): void {\n for (const path of DEFAULT_TRACKED_PROPERTIES) {\n this.#instrumentProperty(path)\n }\n }\n\n #resolveTargetObject(parts: string[]): {\n targetObject: Record<string, unknown>\n propertyName: string\n } {\n // LGraphNode supports dynamic property access at runtime\n let targetObject: Record<string, unknown> = this.node as unknown as Record<\n string,\n unknown\n >\n\n if (parts.length === 1) {\n return { targetObject, propertyName: parts[0] }\n }\n\n for (let i = 0; i < parts.length - 1; i++) {\n const key = parts[i]\n const next = targetObject[key]\n if (isRecord(next)) {\n targetObject = next\n }\n }\n\n return {\n targetObject,\n propertyName: parts[parts.length - 1]\n }\n }\n\n /**\n * Instruments a single property to track changes\n */\n #instrumentProperty(path: string): void {\n const parts = path.split('.')\n\n if (parts.length > 1) {\n this.#ensureNestedPath(path)\n }\n\n const { targetObject, propertyName } = this.#resolveTargetObject(parts)\n\n const hasProperty = Object.prototype.hasOwnProperty.call(\n targetObject,\n propertyName\n )\n const currentValue = targetObject[propertyName]\n\n if (!hasProperty) {\n let value: unknown = undefined\n\n Object.defineProperty(targetObject, propertyName, {\n get: () => value,\n set: (newValue: unknown) => {\n const oldValue = value\n value = newValue\n this.#emitPropertyChange(path, oldValue, newValue)\n\n // Update enumerable: true for non-undefined values, false for undefined\n const shouldBeEnumerable = newValue !== undefined\n const currentDescriptor = Object.getOwnPropertyDescriptor(\n targetObject,\n propertyName\n )\n if (\n currentDescriptor &&\n currentDescriptor.enumerable !== shouldBeEnumerable\n ) {\n Object.defineProperty(targetObject, propertyName, {\n ...currentDescriptor,\n enumerable: shouldBeEnumerable\n })\n }\n },\n enumerable: false,\n configurable: true\n })\n } else {\n Object.defineProperty(\n targetObject,\n propertyName,\n this.#createInstrumentedDescriptor(path, currentValue)\n )\n }\n\n this.#instrumentedPaths.add(path)\n }\n\n /**\n * Creates a property descriptor that emits change events\n */\n #createInstrumentedDescriptor(\n propertyPath: string,\n initialValue: unknown\n ): PropertyDescriptor {\n return this.#createInstrumentedDescriptorTyped(propertyPath, initialValue)\n }\n\n #createInstrumentedDescriptorTyped<TValue>(\n propertyPath: string,\n initialValue: TValue\n ): PropertyDescriptor {\n let value: TValue = initialValue\n\n return {\n get: () => value,\n set: (newValue: TValue) => {\n const oldValue = value\n value = newValue\n this.#emitPropertyChange(propertyPath, oldValue, newValue)\n },\n enumerable: true,\n configurable: true\n }\n }\n\n /**\n * Emits a property change event if the node is connected to a graph\n */\n #emitPropertyChange(\n propertyPath: string,\n oldValue: unknown,\n newValue: unknown\n ): void {\n this.#emitPropertyChangeTyped(propertyPath, oldValue, newValue)\n }\n\n #emitPropertyChangeTyped<TValue>(\n propertyPath: string,\n oldValue: TValue,\n newValue: TValue\n ): void {\n this.node.graph?.trigger('node:property:changed', {\n nodeId: this.node.id,\n property: propertyPath,\n oldValue,\n newValue\n })\n }\n\n /**\n * Ensures parent objects exist for nested properties\n */\n #ensureNestedPath(path: string): void {\n const parts = path.split('.')\n // LGraphNode supports dynamic property access at runtime\n let current: Record<string, unknown> = this.node as unknown as Record<\n string,\n unknown\n >\n\n // Create all parent objects except the last property\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]\n if (!current[part]) {\n current[part] = {}\n }\n const next = current[part]\n if (isRecord(next)) {\n current = next\n }\n }\n }\n\n /**\n * Checks if a property is being tracked\n */\n isTracked(path: string): boolean {\n return this.#instrumentedPaths.has(path)\n }\n\n /**\n * Gets the list of tracked properties\n */\n getTrackedProperties(): string[] {\n return [...DEFAULT_TRACKED_PROPERTIES]\n }\n\n /**\n * Custom toJSON method for JSON.stringify\n * Returns undefined to exclude from serialization since we only use defaults\n */\n toJSON(): undefined {\n return undefined\n }\n}\n","import { without } from 'es-toolkit'\n\nimport type { IColorable, ISlotType } from '@/lib/litegraph/src/interfaces'\n\n/**\n * Converts a plain object to a class instance if it is not already an instance of the class.\n *\n * Requires specific constructor signature; first parameter must be the object to convert.\n * @param cls The class to convert to\n * @param args The object to convert, followed by any other constructor arguments\n * @returns The class instance\n */\nexport function toClass<P, C extends P, Args extends unknown[]>(\n cls: new (instance: P, ...args: Args) => C,\n ...args: [P, ...Args]\n): C {\n return args[0] instanceof cls ? args[0] : new cls(...args)\n}\n\n/**\n * Checks if an object is an instance of {@link IColorable}.\n */\nexport function isColorable(obj: unknown): obj is IColorable {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'setColorOption' in obj &&\n 'getColorOption' in obj\n )\n}\n\nexport function commonType(...types: ISlotType[]): ISlotType | undefined {\n if (!isStrings(types)) return undefined\n\n const withoutWildcards = without(types, '*')\n if (withoutWildcards.length === 0) return '*'\n\n const typeLists: string[][] = withoutWildcards.map((type) => type.split(','))\n\n const combinedTypes = intersection(...typeLists)\n if (combinedTypes.length === 0) return undefined\n\n return combinedTypes.join(',')\n}\n\nfunction intersection(...sets: string[][]): string[] {\n const itemCounts: Record<string, number> = {}\n for (const set of sets)\n for (const item of new Set(set))\n itemCounts[item] = (itemCounts[item] ?? 0) + 1\n return Object.entries(itemCounts)\n .filter(([, count]) => count === sets.length)\n .map(([key]) => key)\n}\n\nfunction isStrings(types: unknown[]): types is string[] {\n return types.every((t) => typeof t === 'string')\n}\n","export interface LGraphIconOptions {\n unicode?: string\n fontFamily?: string\n image?: HTMLImageElement\n color?: string\n bgColor?: string\n fontSize?: number\n size?: number\n circlePadding?: number\n xOffset?: number\n yOffset?: number\n}\n\nexport class LGraphIcon {\n unicode?: string\n fontFamily: string\n image?: HTMLImageElement\n color: string\n bgColor?: string\n fontSize: number\n size: number\n circlePadding: number\n xOffset: number\n yOffset: number\n\n constructor({\n unicode,\n fontFamily = 'PrimeIcons',\n image,\n color = '#e6c200',\n bgColor,\n fontSize = 16,\n size,\n circlePadding = 2,\n xOffset = 0,\n yOffset = 0\n }: LGraphIconOptions) {\n this.unicode = unicode\n this.fontFamily = fontFamily\n this.image = image\n this.color = color\n this.bgColor = bgColor\n this.fontSize = fontSize\n this.size = size ?? fontSize\n this.circlePadding = circlePadding\n this.xOffset = xOffset\n this.yOffset = yOffset\n }\n\n draw(ctx: CanvasRenderingContext2D, x: number, y: number) {\n x += this.xOffset\n y += this.yOffset\n\n if (this.image) {\n const iconSize = this.size\n const iconRadius = iconSize / 2 + this.circlePadding\n\n if (this.bgColor) {\n const { fillStyle } = ctx\n ctx.beginPath()\n ctx.arc(x + iconRadius, y, iconRadius, 0, 2 * Math.PI)\n ctx.fillStyle = this.bgColor\n ctx.fill()\n ctx.fillStyle = fillStyle\n }\n\n const imageX = x + this.circlePadding\n const imageY = y - iconSize / 2\n ctx.drawImage(this.image, imageX, imageY, iconSize, iconSize)\n } else if (this.unicode) {\n const { font, textBaseline, textAlign, fillStyle } = ctx\n\n ctx.font = `${this.fontSize}px '${this.fontFamily}'`\n ctx.textBaseline = 'middle'\n ctx.textAlign = 'center'\n const iconRadius = this.fontSize / 2 + this.circlePadding\n\n if (this.bgColor) {\n ctx.beginPath()\n ctx.arc(x + iconRadius, y, iconRadius, 0, 2 * Math.PI)\n ctx.fillStyle = this.bgColor\n ctx.fill()\n }\n\n ctx.fillStyle = this.color\n ctx.fillText(this.unicode, x + iconRadius, y)\n\n ctx.font = font\n ctx.textBaseline = textBaseline\n ctx.textAlign = textAlign\n ctx.fillStyle = fillStyle\n }\n }\n}\n","import { LGraphIcon } from './LGraphIcon'\nimport type { LGraphIconOptions } from './LGraphIcon'\n\nexport enum BadgePosition {\n TopLeft = 'top-left',\n TopRight = 'top-right'\n}\n\nexport interface LGraphBadgeOptions {\n text: string\n fgColor?: string\n bgColor?: string\n fontSize?: number\n padding?: number\n height?: number\n cornerRadius?: number\n iconOptions?: LGraphIconOptions\n xOffset?: number\n yOffset?: number\n}\n\nexport class LGraphBadge {\n text: string\n fgColor: string\n bgColor: string\n fontSize: number\n padding: number\n height: number\n cornerRadius: number\n icon?: LGraphIcon\n xOffset: number\n yOffset: number\n\n constructor({\n text,\n fgColor = 'white',\n bgColor = '#0F1F0F',\n fontSize = 12,\n padding = 6,\n height = 20,\n cornerRadius = 5,\n iconOptions,\n xOffset = 0,\n yOffset = 0\n }: LGraphBadgeOptions) {\n this.text = text\n this.fgColor = fgColor\n this.bgColor = bgColor\n this.fontSize = fontSize\n this.padding = padding\n this.height = height\n this.cornerRadius = cornerRadius\n if (iconOptions) {\n this.icon = new LGraphIcon(iconOptions)\n }\n this.xOffset = xOffset\n this.yOffset = yOffset\n }\n\n get visible() {\n return (this.text?.length ?? 0) > 0 || !!this.icon\n }\n\n getWidth(ctx: CanvasRenderingContext2D) {\n if (!this.visible) return 0\n const { font } = ctx\n let iconWidth = 0\n if (this.icon) {\n if (this.icon.image) {\n iconWidth = this.icon.size + this.padding\n } else if (this.icon.unicode) {\n ctx.font = `${this.icon.fontSize}px '${this.icon.fontFamily}'`\n iconWidth = ctx.measureText(this.icon.unicode).width + this.padding\n }\n }\n ctx.font = `${this.fontSize}px sans-serif`\n const textWidth = this.text ? ctx.measureText(this.text).width : 0\n ctx.font = font\n return iconWidth + textWidth + this.padding * 2\n }\n\n draw(ctx: CanvasRenderingContext2D, x: number, y: number): void {\n if (!this.visible) return\n\n x += this.xOffset\n y += this.yOffset\n\n const { font, fillStyle, textBaseline, textAlign } = ctx\n\n ctx.font = `${this.fontSize}px sans-serif`\n const badgeWidth = this.getWidth(ctx)\n const badgeX = 0\n\n // Draw badge background\n ctx.fillStyle = this.bgColor\n ctx.beginPath()\n if (ctx.roundRect) {\n ctx.roundRect(x + badgeX, y, badgeWidth, this.height, this.cornerRadius)\n } else {\n // Fallback for browsers that don't support roundRect\n ctx.rect(x + badgeX, y, badgeWidth, this.height)\n }\n ctx.fill()\n\n let drawX = x + badgeX + this.padding\n const centerY = y + this.height / 2\n\n // Draw icon if present\n if (this.icon) {\n this.icon.draw(ctx, drawX, centerY)\n const iconWidth = this.icon.image ? this.icon.size : this.icon.fontSize\n drawX += iconWidth + this.padding / 2 + 4\n }\n\n // Draw badge text\n if (this.text) {\n ctx.fillStyle = this.fgColor\n ctx.textBaseline = 'middle'\n ctx.textAlign = 'left'\n ctx.fillText(this.text, drawX, centerY + 1)\n }\n\n ctx.font = font\n ctx.fillStyle = fillStyle\n ctx.textBaseline = textBaseline\n ctx.textAlign = textAlign\n }\n}\n","import type {\n CompassCorners,\n Point,\n ReadOnlyRect,\n ReadOnlyTypedArray,\n Size\n} from '@/lib/litegraph/src/interfaces'\nimport { isInRectangle } from '@/lib/litegraph/src/measure'\n\n/**\n * A rectangle, represented as a float64 array of 4 numbers: [x, y, width, height].\n *\n * This class is a subclass of Float64Array, and so has all the methods of that class. Notably,\n * {@link Rectangle.from} can be used to convert a {@link ReadOnlyRect}. Typing of this however,\n * is broken due to the base TS lib returning Float64Array rather than `this`.\n *\n * Sub-array properties ({@link Float64Array.subarray}):\n * - {@link pos}: The position of the top-left corner of the rectangle.\n * - {@link size}: The size of the rectangle.\n */\nexport class Rectangle extends Float64Array {\n #pos: Float64Array<ArrayBuffer> | undefined\n #size: Float64Array<ArrayBuffer> | undefined\n\n constructor(\n x: number = 0,\n y: number = 0,\n width: number = 0,\n height: number = 0\n ) {\n super(4)\n\n this[0] = x\n this[1] = y\n this[2] = width\n this[3] = height\n }\n\n static override from([x, y, width, height]: ReadOnlyRect): Rectangle {\n return new Rectangle(x, y, width, height)\n }\n\n /**\n * Creates a new rectangle positioned at the given centre, with the given width/height.\n * @param centre The centre of the rectangle, as an `[x, y]` point\n * @param width The width of the rectangle\n * @param height The height of the rectangle. Default: {@link width}\n * @returns A new rectangle whose centre is at {@link x}\n */\n static fromCentre(\n [x, y]: Readonly<Point>,\n width: number,\n height = width\n ): Rectangle {\n const left = x - width * 0.5\n const top = y - height * 0.5\n return new Rectangle(left, top, width, height)\n }\n\n static ensureRect(rect: ReadOnlyRect): Rectangle {\n return rect instanceof Rectangle\n ? rect\n : new Rectangle(rect[0], rect[1], rect[2], rect[3])\n }\n\n override subarray(\n begin: number = 0,\n end?: number\n ): Float64Array<ArrayBuffer> {\n const byteOffset = begin << 3\n const length = end === undefined ? end : end - begin\n return new Float64Array(this.buffer, byteOffset, length)\n }\n\n /**\n * A reference to the position of the top-left corner of this rectangle.\n *\n * Updating the values of the returned object will update this rectangle.\n */\n get pos(): Point {\n this.#pos ??= this.subarray(0, 2)\n return this.#pos! as unknown as Point\n }\n\n set pos(value: Readonly<Point>) {\n this[0] = value[0]\n this[1] = value[1]\n }\n\n /**\n * A reference to the size of this rectangle.\n *\n * Updating the values of the returned object will update this rectangle.\n */\n get size(): Size {\n this.#size ??= this.subarray(2, 4)\n return this.#size! as unknown as Size\n }\n\n set size(value: Readonly<Size>) {\n this[2] = value[0]\n this[3] = value[1]\n }\n\n // #region Property accessors\n /** The x co-ordinate of the top-left corner of this rectangle. */\n get x() {\n return this[0]\n }\n\n set x(value: number) {\n this[0] = value\n }\n\n /** The y co-ordinate of the top-left corner of this rectangle. */\n get y() {\n return this[1]\n }\n\n set y(value: number) {\n this[1] = value\n }\n\n /** The width of this rectangle. */\n get width() {\n return this[2]\n }\n\n set width(value: number) {\n this[2] = value\n }\n\n /** The height of this rectangle. */\n get height() {\n return this[3]\n }\n\n set height(value: number) {\n this[3] = value\n }\n\n /** The x co-ordinate of the left edge of this rectangle. */\n get left() {\n return this[0]\n }\n\n set left(value: number) {\n this[0] = value\n }\n\n /** The y co-ordinate of the top edge of this rectangle. */\n get top() {\n return this[1]\n }\n\n set top(value: number) {\n this[1] = value\n }\n\n /** The x co-ordinate of the right edge of this rectangle. */\n get right() {\n return this[0] + this[2]\n }\n\n set right(value: number) {\n this[0] = value - this[2]\n }\n\n /** The y co-ordinate of the bottom edge of this rectangle. */\n get bottom() {\n return this[1] + this[3]\n }\n\n set bottom(value: number) {\n this[1] = value - this[3]\n }\n\n /** The x co-ordinate of the centre of this rectangle. */\n get centreX() {\n return this[0] + this[2] * 0.5\n }\n\n /** The y co-ordinate of the centre of this rectangle. */\n get centreY() {\n return this[1] + this[3] * 0.5\n }\n // #endregion Property accessors\n\n /**\n * Updates the rectangle to the values of {@link rect}.\n * @param rect The rectangle to update to.\n */\n updateTo(rect: ReadOnlyRect) {\n this[0] = rect[0]\n this[1] = rect[1]\n this[2] = rect[2]\n this[3] = rect[3]\n }\n\n /**\n * Checks if the point [{@link x}, {@link y}] is inside this rectangle.\n * @param x The x-coordinate to check\n * @param y The y-coordinate to check\n * @returns `true` if the point is inside this rectangle, otherwise `false`.\n */\n containsXy(x: number, y: number): boolean {\n const [left, top, width, height] = this\n return x >= left && x < left + width && y >= top && y < top + height\n }\n\n /**\n * Checks if {@link point} is inside this rectangle.\n * @param point The point to check\n * @returns `true` if {@link point} is inside this rectangle, otherwise `false`.\n */\n containsPoint([x, y]: Readonly<Point>): boolean {\n const [left, top, width, height] = this\n return x >= left && x < left + width && y >= top && y < top + height\n }\n\n /**\n * Checks if {@link other} is a smaller rectangle inside this rectangle.\n * One **must** be larger than the other; identical rectangles are not considered to contain each other.\n * @param other The rectangle to check\n * @returns `true` if {@link other} is inside this rectangle, otherwise `false`.\n */\n containsRect(other: ReadOnlyRect): boolean {\n const { right, bottom } = this\n const otherRight = other[0] + other[2]\n const otherBottom = other[1] + other[3]\n\n const identical =\n this.x === other[0] &&\n this.y === other[1] &&\n right === otherRight &&\n bottom === otherBottom\n\n return (\n !identical &&\n this.x <= other[0] &&\n this.y <= other[1] &&\n right >= otherRight &&\n bottom >= otherBottom\n )\n }\n\n /**\n * Checks if {@link rect} overlaps with this rectangle.\n * @param rect The rectangle to check\n * @returns `true` if {@link rect} overlaps with this rectangle, otherwise `false`.\n */\n overlaps(rect: ReadOnlyRect): boolean {\n return (\n this.x < rect[0] + rect[2] &&\n this.y < rect[1] + rect[3] &&\n this.x + this.width > rect[0] &&\n this.y + this.height > rect[1]\n )\n }\n\n /**\n * Finds the corner (if any) of this rectangle that contains the point [{@link x}, {@link y}].\n * @param x The x-coordinate to check\n * @param y The y-coordinate to check\n * @param cornerSize Each corner is treated as an inset square with this width and height.\n * @returns The compass direction of the corner that contains the point, or `undefined` if the point is not in any corner.\n */\n findContainingCorner(\n x: number,\n y: number,\n cornerSize: number\n ): CompassCorners | undefined {\n if (this.isInTopLeftCorner(x, y, cornerSize)) return 'NW'\n if (this.isInTopRightCorner(x, y, cornerSize)) return 'NE'\n if (this.isInBottomLeftCorner(x, y, cornerSize)) return 'SW'\n if (this.isInBottomRightCorner(x, y, cornerSize)) return 'SE'\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the top-left corner of this rectangle, otherwise `false`. */\n isInTopLeftCorner(x: number, y: number, cornerSize: number): boolean {\n return isInRectangle(x, y, this.x, this.y, cornerSize, cornerSize)\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the top-right corner of this rectangle, otherwise `false`. */\n isInTopRightCorner(x: number, y: number, cornerSize: number): boolean {\n return isInRectangle(\n x,\n y,\n this.right - cornerSize,\n this.y,\n cornerSize,\n cornerSize\n )\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the bottom-left corner of this rectangle, otherwise `false`. */\n isInBottomLeftCorner(x: number, y: number, cornerSize: number): boolean {\n return isInRectangle(\n x,\n y,\n this.x,\n this.bottom - cornerSize,\n cornerSize,\n cornerSize\n )\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the bottom-right corner of this rectangle, otherwise `false`. */\n isInBottomRightCorner(x: number, y: number, cornerSize: number): boolean {\n return isInRectangle(\n x,\n y,\n this.right - cornerSize,\n this.bottom - cornerSize,\n cornerSize,\n cornerSize\n )\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the top edge of this rectangle, otherwise `false`. */\n isInTopEdge(x: number, y: number, edgeSize: number): boolean {\n return isInRectangle(x, y, this.x, this.y, this.width, edgeSize)\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the bottom edge of this rectangle, otherwise `false`. */\n isInBottomEdge(x: number, y: number, edgeSize: number): boolean {\n return isInRectangle(\n x,\n y,\n this.x,\n this.bottom - edgeSize,\n this.width,\n edgeSize\n )\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the left edge of this rectangle, otherwise `false`. */\n isInLeftEdge(x: number, y: number, edgeSize: number): boolean {\n return isInRectangle(x, y, this.x, this.y, edgeSize, this.height)\n }\n\n /** @returns `true` if the point [{@link x}, {@link y}] is in the right edge of this rectangle, otherwise `false`. */\n isInRightEdge(x: number, y: number, edgeSize: number): boolean {\n return isInRectangle(\n x,\n y,\n this.right - edgeSize,\n this.y,\n edgeSize,\n this.height\n )\n }\n\n /** @returns The centre point of this rectangle, as a new {@link Point}. */\n getCentre(): Point {\n return [this.centreX, this.centreY]\n }\n\n /** @returns The area of this rectangle. */\n getArea(): number {\n return this.width * this.height\n }\n\n /** @returns The perimeter of this rectangle. */\n getPerimeter(): number {\n return 2 * (this.width + this.height)\n }\n\n /** @returns The top-left corner of this rectangle, as a new {@link Point}. */\n getTopLeft(): Point {\n return [this[0], this[1]]\n }\n\n /** @returns The bottom-right corner of this rectangle, as a new {@link Point}. */\n getBottomRight(): Point {\n return [this.right, this.bottom]\n }\n\n /** @returns The width and height of this rectangle, as a new {@link Size}. */\n getSize(): Size {\n return [this[2], this[3]]\n }\n\n /** @returns The offset from the top-left of this rectangle to the point [{@link x}, {@link y}], as a new {@link Point}. */\n getOffsetTo([x, y]: Readonly<Point>): Point {\n return [x - this[0], y - this[1]]\n }\n\n /** @returns The offset from the point [{@link x}, {@link y}] to the top-left of this rectangle, as a new {@link Point}. */\n getOffsetFrom([x, y]: Readonly<Point>): Point {\n return [this[0] - x, this[1] - y]\n }\n\n /** Resizes the rectangle without moving it, setting its top-left corner to [{@link x}, {@link y}]. */\n resizeTopLeft(x1: number, y1: number) {\n this[2] += this[0] - x1\n this[3] += this[1] - y1\n\n this[0] = x1\n this[1] = y1\n }\n\n /** Resizes the rectangle without moving it, setting its bottom-left corner to [{@link x}, {@link y}]. */\n resizeBottomLeft(x1: number, y2: number) {\n this[2] += this[0] - x1\n this[3] = y2 - this[1]\n\n this[0] = x1\n }\n\n /** Resizes the rectangle without moving it, setting its top-right corner to [{@link x}, {@link y}]. */\n resizeTopRight(x2: number, y1: number) {\n this[2] = x2 - this[0]\n this[3] += this[1] - y1\n\n this[1] = y1\n }\n\n /** Resizes the rectangle without moving it, setting its bottom-right corner to [{@link x}, {@link y}]. */\n resizeBottomRight(x2: number, y2: number) {\n this[2] = x2 - this[0]\n this[3] = y2 - this[1]\n }\n\n /** Sets the width without moving the right edge (changes position) */\n setWidthRightAnchored(width: number) {\n const currentWidth = this[2]\n this[2] = width\n this[0] += currentWidth - width\n }\n\n /** Sets the height without moving the bottom edge (changes position) */\n setHeightBottomAnchored(height: number) {\n const currentHeight = this[3]\n this[3] = height\n this[1] += currentHeight - height\n }\n\n clone(): Rectangle {\n return new Rectangle(this[0], this[1], this[2], this[3])\n }\n\n /** Alias of {@link export}. */\n toArray() {\n return this.export()\n }\n\n /** @returns A new, untyped array (serializable) containing the values of this rectangle. */\n export(): [number, number, number, number] {\n return [this[0], this[1], this[2], this[3]]\n }\n\n /**\n * Draws a debug outline of this rectangle.\n * @internal Convenience debug/development interface; not for production use.\n */\n _drawDebug(ctx: CanvasRenderingContext2D, colour = 'red') {\n const { strokeStyle, lineWidth } = ctx\n try {\n ctx.strokeStyle = colour\n ctx.lineWidth = 0.5\n ctx.beginPath()\n ctx.strokeRect(this[0], this[1], this[2], this[3])\n } finally {\n ctx.strokeStyle = strokeStyle\n ctx.lineWidth = lineWidth\n }\n }\n}\n\nexport type ReadOnlyRectangle = Omit<\n ReadOnlyTypedArray<Rectangle>,\n | 'setHeightBottomAnchored'\n | 'setWidthRightAnchored'\n | 'resizeTopLeft'\n | 'resizeBottomLeft'\n | 'resizeTopRight'\n | 'resizeBottomRight'\n | 'resizeBottomRight'\n | 'updateTo'\n>\n","import { LGraphBadge } from './LGraphBadge'\nimport type { LGraphBadgeOptions } from './LGraphBadge'\nimport { Rectangle } from './infrastructure/Rectangle'\n\nexport interface LGraphButtonOptions extends LGraphBadgeOptions {\n name?: string // To identify the button\n}\n\nexport class LGraphButton extends LGraphBadge {\n name?: string\n _last_area: Rectangle = new Rectangle()\n\n constructor(options: LGraphButtonOptions) {\n super(options)\n this.name = options.name\n }\n\n override getWidth(ctx: CanvasRenderingContext2D): number {\n if (!this.visible) return 0\n\n const { font } = ctx\n ctx.font = `${this.fontSize}px 'PrimeIcons'`\n\n // For icon buttons, just measure the text width without padding\n const textWidth = this.text ? ctx.measureText(this.text).width : 0\n\n ctx.font = font\n return textWidth\n }\n\n /**\n * @internal\n *\n * Draws the button and updates its last rendered area for hit detection.\n * @param ctx The canvas rendering context.\n * @param x The x-coordinate to draw the button at.\n * @param y The y-coordinate to draw the button at.\n */\n override draw(ctx: CanvasRenderingContext2D, x: number, y: number): void {\n if (!this.visible) {\n return\n }\n\n const width = this.getWidth(ctx)\n\n // Update the hit area\n this._last_area[0] = x + this.xOffset\n this._last_area[1] = y + this.yOffset\n this._last_area[2] = width\n this._last_area[3] = this.height\n\n // Custom drawing for buttons - no background, just icon/text\n const adjustedX = x + this.xOffset\n const adjustedY = y + this.yOffset\n\n const { font, fillStyle, textBaseline, textAlign } = ctx\n\n // Use the same color as the title text (usually white)\n const titleTextColor = ctx.fillStyle || 'white'\n\n // Draw as icon-only without background\n ctx.font = `${this.fontSize}px 'PrimeIcons'`\n ctx.fillStyle = titleTextColor\n ctx.textBaseline = 'middle'\n ctx.textAlign = 'center'\n\n const centerX = adjustedX + width / 2\n const centerY = adjustedY + this.height / 2\n\n if (this.text) {\n ctx.fillText(this.text, centerX, centerY)\n }\n\n // Restore context\n ctx.font = font\n ctx.fillStyle = fillStyle\n ctx.textBaseline = textBaseline\n ctx.textAlign = textAlign\n }\n\n /**\n * Checks if a point is inside the button's last rendered area.\n * @param x The x-coordinate of the point.\n * @param y The y-coordinate of the point.\n * @returns `true` if the point is inside the button, otherwise `false`.\n */\n isPointInside(x: number, y: number): boolean {\n return this._last_area.containsPoint([x, y])\n }\n}\n","import {\n SUBGRAPH_INPUT_ID,\n SUBGRAPH_OUTPUT_ID\n} from '@/lib/litegraph/src/constants'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'\nimport { LayoutSource } from '@/renderer/core/layout/types'\n\nimport type { LGraphNode, NodeId } from './LGraphNode'\nimport type { Reroute, RerouteId } from './Reroute'\nimport type {\n CanvasColour,\n INodeInputSlot,\n INodeOutputSlot,\n ISlotType,\n LinkNetwork,\n LinkSegment,\n Point,\n ReadonlyLinkNetwork\n} from './interfaces'\nimport type { Serialisable, SerialisableLLink } from './types/serialisation'\n\nconst layoutMutations = useLayoutMutations()\n\nexport type LinkId = number\n\nexport type SerialisedLLinkArray = [\n id: LinkId,\n origin_id: NodeId,\n origin_slot: number,\n target_id: NodeId,\n target_slot: number,\n type: ISlotType\n]\n\n// Resolved connection union; eliminates subgraph in/out as a possibility\nexport type ResolvedConnection = BaseResolvedConnection &\n (\n | (ResolvedSubgraphInput & ResolvedNormalOutput)\n | (ResolvedNormalInput & ResolvedSubgraphOutput)\n | (ResolvedNormalInput & ResolvedNormalOutput)\n )\n\ninterface BaseResolvedConnection {\n link: LLink\n /** The node on the input side of the link (owns {@link input}) */\n inputNode?: LGraphNode\n /** The input the link is connected to (mutually exclusive with {@link subgraphOutput}) */\n input?: INodeInputSlot\n /** The node on the output side of the link (owns {@link output}) */\n outputNode?: LGraphNode\n /** The output the link is connected to (mutually exclusive with {@link subgraphInput}) */\n output?: INodeOutputSlot\n /** The subgraph output the link is connected to (mutually exclusive with {@link input}) */\n subgraphOutput?: SubgraphOutput\n /** The subgraph input the link is connected to (mutually exclusive with {@link output}) */\n subgraphInput?: SubgraphInput\n}\n\ninterface ResolvedNormalInput {\n inputNode: LGraphNode | undefined\n input: INodeInputSlot | undefined\n subgraphOutput?: undefined\n}\n\ninterface ResolvedNormalOutput {\n outputNode: LGraphNode | undefined\n output: INodeOutputSlot | undefined\n subgraphInput?: undefined\n}\n\ninterface ResolvedSubgraphInput {\n inputNode?: undefined\n /** The actual input slot the link is connected to (mutually exclusive with {@link subgraphOutput}) */\n input?: undefined\n subgraphOutput: SubgraphOutput\n}\n\ninterface ResolvedSubgraphOutput {\n outputNode?: undefined\n output?: undefined\n subgraphInput: SubgraphInput\n}\n\ntype BasicReadonlyNetwork = Pick<\n ReadonlyLinkNetwork,\n 'getNodeById' | 'links' | 'getLink' | 'inputNode' | 'outputNode'\n>\n\n// this is the class in charge of storing link information\nexport class LLink implements LinkSegment, Serialisable<SerialisableLLink> {\n static _drawDebug = false\n\n /** Link ID */\n id: LinkId\n parentId?: RerouteId\n type: ISlotType\n /** Output node ID */\n origin_id: NodeId\n /** Output slot index */\n origin_slot: number\n /** Input node ID */\n target_id: NodeId\n /** Input slot index */\n target_slot: number\n\n data?: number | string | boolean | { toToolTip?(): string }\n _data?: unknown\n /** Centre point of the link, calculated during render only - can be inaccurate */\n _pos: Point\n /** @todo Clean up - never implemented in comfy. */\n _last_time?: number\n /** The last canvas 2D path that was used to render this link */\n path?: Path2D\n /** @inheritdoc */\n _centreAngle?: number\n\n /** @inheritdoc */\n _dragging?: boolean\n\n #color?: CanvasColour | null\n /** Custom colour for this link only */\n public get color(): CanvasColour | null | undefined {\n return this.#color\n }\n\n public set color(value: CanvasColour) {\n this.#color = value === '' ? null : value\n }\n\n public get isFloatingOutput(): boolean {\n return this.origin_id === -1 && this.origin_slot === -1\n }\n\n public get isFloatingInput(): boolean {\n return this.target_id === -1 && this.target_slot === -1\n }\n\n public get isFloating(): boolean {\n return this.isFloatingOutput || this.isFloatingInput\n }\n\n /** `true` if this link is connected to a subgraph input node (the actual origin is in a different graph). */\n get originIsIoNode(): boolean {\n return this.origin_id === SUBGRAPH_INPUT_ID\n }\n\n /** `true` if this link is connected to a subgraph output node (the actual target is in a different graph). */\n get targetIsIoNode(): boolean {\n return this.target_id === SUBGRAPH_OUTPUT_ID\n }\n\n constructor(\n id: LinkId,\n type: ISlotType,\n origin_id: NodeId,\n origin_slot: number,\n target_id: NodeId,\n target_slot: number,\n parentId?: RerouteId\n ) {\n this.id = id\n this.type = type\n this.origin_id = origin_id\n this.origin_slot = origin_slot\n this.target_id = target_id\n this.target_slot = target_slot\n this.parentId = parentId\n\n this._data = null\n // center\n this._pos = [0, 0]\n }\n\n /** @deprecated Use {@link LLink.create} */\n static createFromArray(data: SerialisedLLinkArray): LLink {\n return new LLink(data[0], data[5], data[1], data[2], data[3], data[4])\n }\n\n /**\n * LLink static factory: creates a new LLink from the provided data.\n * @param data Serialised LLink data to create the link from\n * @returns A new LLink\n */\n static create(data: SerialisableLLink): LLink {\n return new LLink(\n data.id,\n data.type,\n data.origin_id,\n data.origin_slot,\n data.target_id,\n data.target_slot,\n data.parentId\n )\n }\n\n /**\n * Gets all reroutes from the output slot to this segment. If this segment is a reroute, it will not be included.\n * @returns An ordered array of all reroutes from the node output to\n * this reroute or the reroute before it. Otherwise, an empty array.\n */\n static getReroutes(\n network: Pick<ReadonlyLinkNetwork, 'reroutes'>,\n linkSegment: LinkSegment\n ): Reroute[] {\n if (linkSegment.parentId === undefined) return []\n return network.reroutes.get(linkSegment.parentId)?.getReroutes() ?? []\n }\n\n static getFirstReroute(\n network: Pick<ReadonlyLinkNetwork, 'reroutes'>,\n linkSegment: LinkSegment\n ): Reroute | undefined {\n return LLink.getReroutes(network, linkSegment).at(0)\n }\n\n /**\n * Finds the reroute in the chain after the provided reroute ID.\n * @param network The network this link belongs to\n * @param linkSegment The starting point of the search (input side).\n * Typically the LLink object itself, but can be any link segment.\n * @param rerouteId The matching reroute will have this set as its {@link parentId}.\n * @returns The reroute that was found, `undefined` if no reroute was found, or `null` if an infinite loop was detected.\n */\n static findNextReroute(\n network: Pick<ReadonlyLinkNetwork, 'reroutes'>,\n linkSegment: LinkSegment,\n rerouteId: RerouteId\n ): Reroute | null | undefined {\n if (linkSegment.parentId === undefined) return\n return network.reroutes\n .get(linkSegment.parentId)\n ?.findNextReroute(rerouteId)\n }\n\n /**\n * Gets the origin node of a link.\n * @param network The network to search\n * @param linkId The ID of the link to get the origin node of\n * @returns The origin node of the link, or `undefined` if the link is not found or the origin node is not found\n */\n static getOriginNode(\n network: BasicReadonlyNetwork,\n linkId: LinkId\n ): LGraphNode | undefined {\n const id = network.links.get(linkId)?.origin_id\n return network.getNodeById(id) ?? undefined\n }\n\n /**\n * Gets the target node of a link.\n * @param network The network to search\n * @param linkId The ID of the link to get the target node of\n * @returns The target node of the link, or `undefined` if the link is not found or the target node is not found\n */\n static getTargetNode(\n network: BasicReadonlyNetwork,\n linkId: LinkId\n ): LGraphNode | undefined {\n const id = network.links.get(linkId)?.target_id\n return network.getNodeById(id) ?? undefined\n }\n\n /**\n * Resolves a link ID to the link, node, and slot objects.\n * @param linkId The {@link id} of the link to resolve\n * @param network The link network to search\n * @returns An object containing the input and output nodes, as well as the input and output slots.\n * @remarks This method is heavier than others; it will always resolve all objects.\n * Whilst the performance difference should in most cases be negligible,\n * it is recommended to use simpler methods where appropriate.\n */\n static resolve(\n linkId: LinkId | null | undefined,\n network: BasicReadonlyNetwork\n ): ResolvedConnection | undefined {\n return network.getLink(linkId)?.resolve(network)\n }\n\n /**\n * Resolves a list of link IDs to the link, node, and slot objects.\n * Discards invalid link IDs.\n * @param linkIds An iterable of link {@link id}s to resolve\n * @param network The link network to search\n * @returns An array of resolved connections. If a link is not found, it is not included in the array.\n * @see {@link LLink.resolve}\n */\n static resolveMany(\n linkIds: Iterable<LinkId>,\n network: BasicReadonlyNetwork\n ): ResolvedConnection[] {\n const resolved: ResolvedConnection[] = []\n for (const id of linkIds) {\n const r = network.getLink(id)?.resolve(network)\n if (r) resolved.push(r)\n }\n return resolved\n }\n\n /**\n * Resolves the primitive ID values stored in the link to the referenced objects.\n * @param network The link network to search\n * @returns An object containing the input and output nodes, as well as the input and output slots.\n * @remarks This method is heavier than others; it will always resolve all objects.\n * Whilst the performance difference should in most cases be negligible,\n * it is recommended to use simpler methods where appropriate.\n */\n resolve(network: BasicReadonlyNetwork): ResolvedConnection {\n const inputNode =\n this.target_id === -1\n ? undefined\n : (network.getNodeById(this.target_id) ?? undefined)\n const input = inputNode?.inputs[this.target_slot]\n const subgraphInput = this.originIsIoNode\n ? network.inputNode?.slots[this.origin_slot]\n : undefined\n if (subgraphInput) {\n return { inputNode, input, subgraphInput, link: this }\n }\n\n const outputNode =\n this.origin_id === -1\n ? undefined\n : (network.getNodeById(this.origin_id) ?? undefined)\n const output = outputNode?.outputs[this.origin_slot]\n const subgraphOutput = this.targetIsIoNode\n ? network.outputNode?.slots[this.target_slot]\n : undefined\n if (subgraphOutput) {\n return {\n outputNode,\n output,\n subgraphInput: undefined,\n subgraphOutput,\n link: this\n }\n }\n\n return {\n inputNode,\n outputNode,\n input,\n output,\n subgraphInput,\n subgraphOutput,\n link: this\n }\n }\n\n configure(o: LLink | SerialisedLLinkArray) {\n if (Array.isArray(o)) {\n this.id = o[0]\n this.origin_id = o[1]\n this.origin_slot = o[2]\n this.target_id = o[3]\n this.target_slot = o[4]\n this.type = o[5]\n } else {\n this.id = o.id\n this.type = o.type\n this.origin_id = o.origin_id\n this.origin_slot = o.origin_slot\n this.target_id = o.target_id\n this.target_slot = o.target_slot\n this.parentId = o.parentId\n }\n }\n\n /**\n * Checks if the specified node id and output index are this link's origin (output side).\n * @param nodeId ID of the node to check\n * @param outputIndex The array index of the node output\n * @returns `true` if the origin matches, otherwise `false`.\n */\n hasOrigin(nodeId: NodeId, outputIndex: number): boolean {\n return this.origin_id === nodeId && this.origin_slot === outputIndex\n }\n\n /**\n * Checks if the specified node id and input index are this link's target (input side).\n * @param nodeId ID of the node to check\n * @param inputIndex The array index of the node input\n * @returns `true` if the target matches, otherwise `false`.\n */\n hasTarget(nodeId: NodeId, inputIndex: number): boolean {\n return this.target_id === nodeId && this.target_slot === inputIndex\n }\n\n /**\n * Creates a floating link from this link.\n * @param slotType The side of the link that is still connected\n * @param parentId The parent reroute ID of the link\n * @returns A new LLink that is floating\n */\n toFloating(slotType: 'input' | 'output', parentId: RerouteId): LLink {\n const exported = this.asSerialisable()\n exported.id = -1\n exported.parentId = parentId\n\n if (slotType === 'input') {\n exported.origin_id = -1\n exported.origin_slot = -1\n } else {\n exported.target_id = -1\n exported.target_slot = -1\n }\n\n return LLink.create(exported)\n }\n\n /**\n * Disconnects a link and removes it from the graph, cleaning up any reroutes that are no longer used\n * @param network The container (LGraph) where reroutes should be updated\n * @param keepReroutes If `undefined`, reroutes will be automatically removed if no links remain.\n * If `input` or `output`, reroutes will not be automatically removed, and retain a connection to the input or output, respectively.\n */\n disconnect(network: LinkNetwork, keepReroutes?: 'input' | 'output'): void {\n const reroutes = LLink.getReroutes(network, this)\n\n const lastReroute = reroutes.at(-1)\n\n // When floating from output, 1-to-1 ratio of floating link to final reroute (tree-like)\n const outputFloating =\n keepReroutes === 'output' &&\n lastReroute?.linkIds.size === 1 &&\n lastReroute.floatingLinkIds.size === 0\n\n // When floating from inputs, the final (input side) reroute may have many floating links\n if (outputFloating || (keepReroutes === 'input' && lastReroute)) {\n const newLink = LLink.create(this)\n newLink.id = -1\n\n if (keepReroutes === 'input') {\n newLink.origin_id = -1\n newLink.origin_slot = -1\n\n lastReroute.floating = { slotType: 'input' }\n } else {\n newLink.target_id = -1\n newLink.target_slot = -1\n\n lastReroute.floating = { slotType: 'output' }\n }\n\n network.addFloatingLink(newLink)\n }\n\n for (const reroute of reroutes) {\n reroute.linkIds.delete(this.id)\n if (!keepReroutes && !reroute.totalLinks) {\n network.reroutes.delete(reroute.id)\n // Delete reroute from Layout Store\n layoutMutations.setSource(LayoutSource.Canvas)\n layoutMutations.deleteReroute(reroute.id)\n }\n }\n network.links.delete(this.id)\n // Delete link from Layout Store\n layoutMutations.setSource(LayoutSource.Canvas)\n layoutMutations.deleteLink(this.id)\n }\n\n /**\n * @deprecated Prefer {@link LLink.asSerialisable} (returns an object, not an array)\n * @returns An array representing this LLink\n */\n serialize(): SerialisedLLinkArray {\n return [\n this.id,\n this.origin_id,\n this.origin_slot,\n this.target_id,\n this.target_slot,\n this.type\n ]\n }\n\n asSerialisable(): SerialisableLLink {\n const copy: SerialisableLLink = {\n id: this.id,\n origin_id: this.origin_id,\n origin_slot: this.origin_slot,\n target_id: this.target_id,\n target_slot: this.target_slot,\n type: this.type\n }\n if (this.parentId !== undefined) copy.parentId = this.parentId\n return copy\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport { isInRectangle } from '@/lib/litegraph/src/measure'\n\nexport function getNodeInputOnPos(\n node: LGraphNode,\n x: number,\n y: number\n): { index: number; input: INodeInputSlot; pos: Point } | undefined {\n const { inputs } = node\n if (!inputs) return\n\n for (const [index, input] of inputs.entries()) {\n const pos = node.getInputPos(index)\n\n // TODO: Find a cheap way to measure text, and do it on node label change instead of here\n // Input icon width + text approximation\n const nameLength =\n input.label?.length ?? input.localized_name?.length ?? input.name?.length\n const width = 20 + (nameLength || 3) * 7\n\n if (isInRectangle(x, y, pos[0] - 10, pos[1] - 10, width, 20)) {\n return { index, input, pos }\n }\n }\n}\n\nexport function getNodeOutputOnPos(\n node: LGraphNode,\n x: number,\n y: number\n): { index: number; output: INodeOutputSlot; pos: Point } | undefined {\n const { outputs } = node\n if (!outputs) return\n\n for (const [index, output] of outputs.entries()) {\n const pos = node.getOutputPos(index)\n\n if (isInRectangle(x, y, pos[0] - 10, pos[1] - 10, 40, 20)) {\n return { index, output, pos }\n }\n }\n}\n\n/**\n * Returns the input slot index if the given position (in graph space) is on top of a node input slot.\n * A helper function - originally on the prototype of LGraphCanvas.\n */\nexport function isOverNodeInput(\n node: LGraphNode,\n canvasx: number,\n canvasy: number,\n slot_pos?: Point\n): number {\n const result = getNodeInputOnPos(node, canvasx, canvasy)\n if (!result) return -1\n\n if (slot_pos) {\n slot_pos[0] = result.pos[0]\n slot_pos[1] = result.pos[1]\n }\n return result.index\n}\n\n/**\n * Returns the output slot index if the given position (in graph space) is on top of a node output slot.\n * A helper function - originally on the prototype of LGraphCanvas.\n */\nexport function isOverNodeOutput(\n node: LGraphNode,\n canvasx: number,\n canvasy: number,\n slot_pos?: Point\n): number {\n const result = getNodeOutputOnPos(node, canvasx, canvasy)\n if (!result) return -1\n\n if (slot_pos) {\n slot_pos[0] = result.pos[0]\n slot_pos[1] = result.pos[1]\n }\n return result.index\n}\n","import type { Rectangle } from './infrastructure/Rectangle'\nimport type { CanvasColour } from './interfaces'\nimport { LiteGraph } from './litegraph'\nimport { RenderShape, TitleMode } from './types/globalEnums'\n\nconst ELLIPSIS = '\\u2026'\nconst TWO_DOT_LEADER = '\\u2025'\nconst ONE_DOT_LEADER = '\\u2024'\n\nexport enum SlotType {\n Array = 'array',\n Event = -1\n}\n\n/** @see RenderShape */\nexport enum SlotShape {\n Box = RenderShape.BOX,\n Arrow = RenderShape.ARROW,\n Grid = RenderShape.GRID,\n Circle = RenderShape.CIRCLE,\n HollowCircle = RenderShape.HollowCircle\n}\n\n/** @see LinkDirection */\nexport enum SlotDirection {}\n\nexport enum LabelPosition {\n Left = 'left',\n Right = 'right'\n}\n\nexport interface IDrawBoundingOptions {\n /** The shape to render */\n shape?: RenderShape\n /** The radius of the rounded corners for {@link RenderShape.ROUND} and {@link RenderShape.CARD} */\n round_radius?: number\n /** Shape will extend above the Y-axis 0 by this amount @deprecated This is node-specific: it should be removed entirely, and behaviour defined by the caller more explicitly */\n title_height?: number\n /** @deprecated This is node-specific: it should be removed entirely, and behaviour defined by the caller more explicitly */\n title_mode?: TitleMode\n /** The color that should be drawn */\n color?: CanvasColour\n /** The distance between the edge of the {@link area} and the middle of the line */\n padding?: number\n /** @deprecated This is node-specific: it should be removed entirely, and behaviour defined by the caller more explicitly */\n collapsed?: boolean\n /** Thickness of the line drawn (`lineWidth`) */\n lineWidth?: number\n}\n\ninterface IDrawTextInAreaOptions {\n /** The canvas to draw the text on. */\n ctx: CanvasRenderingContext2D\n /** The text to draw. */\n text: string\n /** The area the text will be drawn in. */\n area: Rectangle\n /** The alignment of the text. */\n align?: 'left' | 'right' | 'center'\n}\n\n/**\n * Draws only the path of a shape on the canvas, without filling.\n * Used to draw indicators for node status, e.g. \"selected\".\n * @param ctx The 2D context to draw on\n * @param area The position and size of the shape to render\n */\nexport function strokeShape(\n ctx: CanvasRenderingContext2D,\n area: Rectangle,\n {\n shape = RenderShape.BOX,\n round_radius,\n title_height,\n title_mode = TitleMode.NORMAL_TITLE,\n color,\n padding = 6,\n collapsed = false,\n lineWidth: thickness = 1\n }: IDrawBoundingOptions = {}\n): void {\n // These param defaults are not compile-time static, and must be re-evaluated at runtime\n round_radius ??= LiteGraph.ROUND_RADIUS\n color ??= LiteGraph.NODE_BOX_OUTLINE_COLOR\n\n // Adjust area if title is transparent\n if (title_mode === TitleMode.TRANSPARENT_TITLE) {\n const height = title_height ?? LiteGraph.NODE_TITLE_HEIGHT\n area[1] -= height\n area[3] += height\n }\n\n // Set up context\n const { lineWidth, strokeStyle } = ctx\n ctx.lineWidth = thickness\n ctx.globalAlpha = 0.8\n ctx.strokeStyle = color\n ctx.beginPath()\n\n // Draw shape based on type\n const [x, y, width, height] = area\n switch (shape) {\n case RenderShape.BOX: {\n ctx.rect(\n x - padding,\n y - padding,\n width + 2 * padding,\n height + 2 * padding\n )\n break\n }\n case RenderShape.ROUND:\n case RenderShape.CARD: {\n const radius = round_radius + padding\n const isCollapsed = shape === RenderShape.CARD && collapsed\n const cornerRadii =\n isCollapsed || shape === RenderShape.ROUND\n ? [radius]\n : [radius, 2, radius, 2]\n ctx.roundRect(\n x - padding,\n y - padding,\n width + 2 * padding,\n height + 2 * padding,\n cornerRadii\n )\n break\n }\n case RenderShape.CIRCLE: {\n const centerX = x + width / 2\n const centerY = y + height / 2\n const radius = Math.max(width, height) / 2 + padding\n ctx.arc(centerX, centerY, radius, 0, Math.PI * 2)\n break\n }\n }\n\n // Stroke the shape\n ctx.stroke()\n\n // Reset context\n ctx.lineWidth = lineWidth\n ctx.strokeStyle = strokeStyle\n\n // TODO: Store and reset value properly. Callers currently expect this behaviour (e.g. muted nodes).\n ctx.globalAlpha = 1\n}\n\n/**\n * Truncates text using binary search to fit within a given width, appending an ellipsis if needed.\n * @param ctx The canvas rendering context.\n * @param text The text to truncate.\n * @param maxWidth The maximum width the text (plus ellipsis) can occupy.\n * @returns The truncated text, or the original text if it fits.\n */\nfunction truncateTextToWidth(\n ctx: CanvasRenderingContext2D,\n text: string,\n maxWidth: number\n): string {\n if (!(maxWidth > 0)) return ''\n\n // Text fits\n const fullWidth = ctx.measureText(text).width\n if (fullWidth <= maxWidth) return text\n\n const ellipsisWidth = ctx.measureText(ELLIPSIS).width * 0.75\n\n // Can't even fit ellipsis\n if (ellipsisWidth > maxWidth) {\n const twoDotsWidth = ctx.measureText(TWO_DOT_LEADER).width * 0.75\n if (twoDotsWidth < maxWidth) return TWO_DOT_LEADER\n\n const oneDotWidth = ctx.measureText(ONE_DOT_LEADER).width * 0.75\n return oneDotWidth < maxWidth ? ONE_DOT_LEADER : ''\n }\n\n let min = 0\n let max = text.length\n let bestLen = 0\n\n // Binary search for the longest substring that fits with the ellipsis\n while (min <= max) {\n const mid = Math.floor((min + max) * 0.5)\n\n // Avoid measuring empty string + ellipsis\n if (mid === 0) {\n min = mid + 1\n continue\n }\n\n const sub = text.substring(0, mid)\n const currentWidth = ctx.measureText(sub).width + ellipsisWidth\n\n if (currentWidth <= maxWidth) {\n // This length fits, try potentially longer\n bestLen = mid\n min = mid + 1\n } else {\n // Too long, try shorter\n max = mid - 1\n }\n }\n\n return bestLen === 0 ? ELLIPSIS : text.substring(0, bestLen) + ELLIPSIS\n}\n\n/**\n * Draws text within an area, truncating it and adding an ellipsis if necessary.\n */\nexport function drawTextInArea({\n ctx,\n text,\n area,\n align = 'left'\n}: IDrawTextInAreaOptions) {\n const { left, right, bottom, width, centreX } = area\n\n // Text already fits\n const fullWidth = ctx.measureText(text).width\n if (fullWidth <= width) {\n ctx.textAlign = align\n const x = align === 'left' ? left : align === 'right' ? right : centreX\n ctx.fillText(text, x, bottom)\n return\n }\n\n // Need to truncate text\n const truncated = truncateTextToWidth(ctx, text, width)\n if (truncated.length === 0) return\n\n // Draw text - left-aligned to prevent bouncing during resize\n ctx.textAlign = 'left'\n ctx.fillText(truncated.slice(0, -1), left, bottom)\n ctx.rect(left, bottom, width, 1)\n\n // Draw the ellipsis, right-aligned to the button\n ctx.textAlign = 'right'\n const ellipsis = truncated.at(-1)!\n ctx.fillText(ellipsis, right, bottom, ctx.measureText(ellipsis).width * 0.75)\n}\n","import type { LLink } from '@/lib/litegraph/src/LLink'\nimport { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'\nimport type {\n CanvasColour,\n DefaultConnectionColors,\n INodeSlot,\n ISlotType,\n IWidgetLocator,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type {\n LinkDirection,\n RenderShape\n} from '@/lib/litegraph/src/types/globalEnums'\n\n/** Base class for all input & output slots. */\n\nexport abstract class SlotBase implements INodeSlot {\n name: string\n localized_name?: string\n label?: string\n type: ISlotType\n dir?: LinkDirection\n removable?: boolean\n shape?: RenderShape\n color_off?: CanvasColour\n color_on?: CanvasColour\n locked?: boolean\n nameLocked?: boolean\n widget?: IWidgetLocator\n _floatingLinks?: Set<LLink>\n hasErrors?: boolean\n\n /** The centre point of the slot. */\n abstract pos?: Point\n readonly boundingRect: Rectangle\n\n constructor(name: string, type: ISlotType, boundingRect?: Rectangle) {\n this.name = name\n this.type = type\n this.boundingRect = boundingRect ?? new Rectangle()\n }\n\n abstract get isConnected(): boolean\n\n renderingColor(colorContext: DefaultConnectionColors): CanvasColour {\n return this.isConnected\n ? this.color_on || colorContext.getConnectedColor(this.type)\n : this.color_off || colorContext.getDisconnectedColor(this.type)\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { LabelPosition, SlotShape, SlotType } from '@/lib/litegraph/src/draw'\nimport type {\n CanvasColour,\n DefaultConnectionColors,\n INodeInputSlot,\n INodeOutputSlot,\n INodeSlot,\n ISubgraphInput,\n OptionalProps,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph, Rectangle } from '@/lib/litegraph/src/litegraph'\nimport { getCentre } from '@/lib/litegraph/src/measure'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport {\n LinkDirection,\n RenderShape\n} from '@/lib/litegraph/src/types/globalEnums'\n\nimport { NodeInputSlot } from './NodeInputSlot'\nimport { SlotBase } from './SlotBase'\n\nexport interface IDrawOptions {\n colorContext: DefaultConnectionColors\n labelPosition?: LabelPosition\n lowQuality?: boolean\n doStroke?: boolean\n highlight?: boolean\n}\n\nconst ROTATION_OFFSET = -Math.PI / 2\n\n/** Shared base class for {@link LGraphNode} input and output slots. */\nexport abstract class NodeSlot extends SlotBase implements INodeSlot {\n pos?: Point\n\n /** The offset from the parent node to the centre point of this slot. */\n get #centreOffset(): Readonly<Point> {\n const nodePos = this.node.pos\n const { boundingRect } = this\n\n // Use height; widget input slots may be thinner.\n const diameter = boundingRect[3]\n\n return getCentre([\n boundingRect[0] - nodePos[0],\n boundingRect[1] - nodePos[1],\n diameter,\n diameter\n ])\n }\n\n /** The center point of this slot when the node is collapsed. */\n abstract get collapsedPos(): Readonly<Point>\n\n #node: LGraphNode\n get node(): LGraphNode {\n return this.#node\n }\n\n get highlightColor(): CanvasColour {\n return (\n LiteGraph.NODE_TEXT_HIGHLIGHT_COLOR ??\n LiteGraph.NODE_SELECTED_TITLE_COLOR ??\n LiteGraph.NODE_TEXT_COLOR\n )\n }\n\n abstract get isWidgetInputSlot(): boolean\n\n constructor(\n slot: OptionalProps<INodeSlot, 'boundingRect'>,\n node: LGraphNode\n ) {\n // @ts-expect-error Workaround: Ensure internal properties are not copied to the slot (_listenerController\n // https://github.com/Comfy-Org/litegraph.js/issues/1138\n const maybeSubgraphSlot: OptionalProps<\n ISubgraphInput,\n 'link' | 'boundingRect'\n > = slot\n const { boundingRect, name, type, _listenerController, ...rest } =\n maybeSubgraphSlot\n const rectangle = boundingRect\n ? Rectangle.ensureRect(boundingRect)\n : new Rectangle()\n\n super(name, type, rectangle)\n\n Object.assign(this, rest)\n this.#node = node\n }\n\n /**\n * Whether this slot is a valid target for a dragging link.\n * @param fromSlot The slot that the link is being connected from.\n */\n abstract isValidTarget(\n fromSlot: INodeInputSlot | INodeOutputSlot | SubgraphInput | SubgraphOutput\n ): boolean\n\n /**\n * The label to display in the UI.\n */\n get renderingLabel(): string {\n return this.label || this.localized_name || this.name || ''\n }\n\n draw(\n ctx: CanvasRenderingContext2D,\n {\n colorContext,\n labelPosition = LabelPosition.Right,\n lowQuality = false,\n highlight = false,\n doStroke = false\n }: IDrawOptions\n ) {\n // Save the current fillStyle and strokeStyle\n const originalFillStyle = ctx.fillStyle\n const originalStrokeStyle = ctx.strokeStyle\n const originalLineWidth = ctx.lineWidth\n\n const labelColor = highlight\n ? this.highlightColor\n : LiteGraph.NODE_TEXT_COLOR\n\n const pos = this.#centreOffset\n const slot_type = this.type\n const slot_shape = (\n slot_type === SlotType.Array ? SlotShape.Grid : this.shape\n ) as SlotShape\n\n ctx.save()\n ctx.beginPath()\n let doFill = true\n\n ctx.fillStyle = this.renderingColor(colorContext)\n ctx.lineWidth = 1\n if (slot_type === SlotType.Event || slot_shape === SlotShape.Box) {\n ctx.rect(pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, 10)\n } else if (slot_shape === SlotShape.Arrow) {\n ctx.moveTo(pos[0] + 8, pos[1] + 0.5)\n ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5)\n ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5)\n ctx.closePath()\n } else if (slot_shape === SlotShape.Grid) {\n const gridSize = 3\n const cellSize = 2\n const spacing = 3\n\n for (let x = 0; x < gridSize; x++) {\n for (let y = 0; y < gridSize; y++) {\n ctx.rect(\n pos[0] - 4 + x * spacing,\n pos[1] - 4 + y * spacing,\n cellSize,\n cellSize\n )\n }\n }\n doStroke = false\n } else {\n // Default rendering for circle, hollow circle.\n if (lowQuality) {\n ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8)\n } else {\n if (slot_shape === SlotShape.HollowCircle) {\n const path = new Path2D()\n path.arc(pos[0], pos[1], 10, 0, Math.PI * 2)\n path.arc(pos[0], pos[1], highlight ? 2.5 : 1.5, 0, Math.PI * 2)\n ctx.clip(path, 'evenodd')\n }\n const radius = highlight ? 5 : 4\n const typesSet = new Set(\n `${this.type}`\n .split(',')\n .map(\n this.isConnected\n ? (type) => colorContext.getConnectedColor(type)\n : (type) => colorContext.getDisconnectedColor(type)\n )\n )\n const types = [...typesSet].slice(0, 3)\n if (types.length > 1) {\n doFill = false\n const arcLen = (Math.PI * 2) / types.length\n types.forEach((type, idx) => {\n ctx.moveTo(pos[0], pos[1])\n ctx.fillStyle = type\n ctx.arc(\n pos[0],\n pos[1],\n radius,\n arcLen * idx + ROTATION_OFFSET,\n Math.PI * 2 + ROTATION_OFFSET\n )\n ctx.fill()\n ctx.beginPath()\n })\n //add stroke dividers\n ctx.save()\n ctx.strokeStyle = 'black'\n ctx.lineWidth = 0.5\n types.forEach((_, idx) => {\n ctx.moveTo(pos[0], pos[1])\n const xOffset = Math.cos(arcLen * idx + ROTATION_OFFSET) * radius\n const yOffset = Math.sin(arcLen * idx + ROTATION_OFFSET) * radius\n ctx.lineTo(pos[0] + xOffset, pos[1] + yOffset)\n })\n ctx.stroke()\n ctx.restore()\n ctx.beginPath()\n }\n ctx.arc(pos[0], pos[1], radius, 0, Math.PI * 2)\n }\n }\n\n if (doFill) ctx.fill()\n if (!lowQuality && doStroke) ctx.stroke()\n ctx.restore()\n\n // render slot label\n const hideLabel = lowQuality || this.isWidgetInputSlot\n if (!hideLabel) {\n const text = this.renderingLabel\n if (text) {\n // TODO: Finish impl. Highlight text on mouseover unless we're connecting links.\n ctx.fillStyle = labelColor\n\n if (labelPosition === LabelPosition.Right) {\n if (this.dir == LinkDirection.UP) {\n ctx.fillText(text, pos[0], pos[1] - 10)\n } else {\n ctx.fillText(text, pos[0] + 10, pos[1] + 5)\n }\n } else {\n if (this.dir == LinkDirection.DOWN) {\n ctx.fillText(text, pos[0], pos[1] - 8)\n } else {\n ctx.fillText(text, pos[0] - 10, pos[1] + 5)\n }\n }\n }\n }\n\n // Draw a red circle if the slot has errors.\n if (this.hasErrors) {\n ctx.lineWidth = 2\n ctx.strokeStyle = 'red'\n ctx.beginPath()\n ctx.arc(pos[0], pos[1], 12, 0, Math.PI * 2)\n ctx.stroke()\n }\n\n // Restore the original fillStyle and strokeStyle\n ctx.fillStyle = originalFillStyle\n ctx.strokeStyle = originalStrokeStyle\n ctx.lineWidth = originalLineWidth\n }\n\n drawCollapsed(ctx: CanvasRenderingContext2D) {\n const [x, y] = this.collapsedPos\n\n // Save original styles\n const { fillStyle } = ctx\n\n ctx.fillStyle = '#686'\n ctx.beginPath()\n\n if (this.type === SlotType.Event || this.shape === RenderShape.BOX) {\n ctx.rect(x - 7 + 0.5, y - 4, 14, 8)\n } else if (this.shape === RenderShape.ARROW) {\n // Adjust arrow direction based on whether this is an input or output slot\n const isInput = this instanceof NodeInputSlot\n if (isInput) {\n ctx.moveTo(x + 8, y)\n ctx.lineTo(x - 4, y - 4)\n ctx.lineTo(x - 4, y + 4)\n } else {\n ctx.moveTo(x + 6, y)\n ctx.lineTo(x - 6, y - 4)\n ctx.lineTo(x - 6, y + 4)\n }\n ctx.closePath()\n } else {\n ctx.arc(x, y, 4, 0, Math.PI * 2)\n }\n ctx.fill()\n\n // Restore original styles\n ctx.fillStyle = fillStyle\n }\n}\n","import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'\nimport { LayoutSource } from '@/renderer/core/layout/types'\n\nimport { LGraphBadge } from './LGraphBadge'\nimport type { LGraphNode, NodeId } from './LGraphNode'\nimport { LLink } from './LLink'\nimport type { LinkId } from './LLink'\nimport type {\n CanvasColour,\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n LinkSegment,\n Point,\n Positionable,\n ReadOnlyRect,\n ReadonlyLinkNetwork\n} from './interfaces'\nimport { distance, isPointInRect } from './measure'\nimport type { Serialisable, SerialisableReroute } from './types/serialisation'\n\nconst layoutMutations = useLayoutMutations()\n\nexport type RerouteId = number\n\n/** The input or output slot that an incomplete reroute link is connected to. */\nexport interface FloatingRerouteSlot {\n /** Floating connection to an input or output */\n slotType: 'input' | 'output'\n}\n\n/**\n * Represents an additional point on the graph that a link path will travel through. Used for visual organisation only.\n *\n * Requires no disposal or clean up.\n * Stores only primitive values (IDs) to reference other items in its network,\n * and a `WeakRef` to a {@link LinkNetwork} to resolve them.\n */\nexport class Reroute\n implements Positionable, LinkSegment, Serialisable<SerialisableReroute>\n{\n static radius: number = 10\n /** Maximum distance from reroutes to their bezier curve control points. */\n static maxSplineOffset: number = 80\n static drawIdBadge: boolean = false\n static slotRadius: number = 5\n /** Distance from reroute centre to slot centre. */\n static get slotOffset(): number {\n const gap = Reroute.slotRadius * 0.33\n return Reroute.radius + gap + Reroute.slotRadius\n }\n\n /** The network this reroute belongs to. Contains all valid links and reroutes. */\n private readonly network: WeakRef<LinkNetwork>\n\n private parentIdInternal?: RerouteId\n public get parentId(): RerouteId | undefined {\n return this.parentIdInternal\n }\n\n /** Ignores attempts to create an infinite loop. @inheritdoc */\n public set parentId(value) {\n if (value === this.id) return\n if (this.getReroutes() === null) return\n this.parentIdInternal = value\n }\n\n public get parent(): Reroute | undefined {\n return this.network.deref()?.getReroute(this.parentIdInternal)\n }\n\n /** This property is only defined on the last reroute of a floating reroute chain (closest to input end). */\n floating?: FloatingRerouteSlot\n\n private readonly posInternal: Point = [0, 0]\n /** @inheritdoc */\n get pos(): Point {\n return this.posInternal\n }\n\n set pos(value: Point) {\n if (!(value?.length >= 2))\n throw new TypeError(\n 'Reroute.pos is an x,y point, and expects an indexable with at least two values.'\n )\n this.posInternal[0] = value[0]\n this.posInternal[1] = value[1]\n }\n\n /** @inheritdoc */\n get boundingRect(): ReadOnlyRect {\n const { radius } = Reroute\n const [x, y] = this.posInternal\n return [x - radius, y - radius, 2 * radius, 2 * radius]\n }\n\n /**\n * Slightly over-sized rectangle, guaranteed to contain the entire surface area for hover detection.\n * Eliminates most hover positions using an extremely cheap check.\n */\n private get hoverArea(): ReadOnlyRect {\n const xOffset = 2 * Reroute.slotOffset\n const yOffset = 2 * Math.max(Reroute.radius, Reroute.slotRadius)\n\n const [x, y] = this.posInternal\n return [x - xOffset, y - yOffset, 2 * xOffset, 2 * yOffset]\n }\n\n /** The total number of links & floating links using this reroute */\n get totalLinks(): number {\n return this.linkIds.size + this.floatingLinkIds.size\n }\n\n /** @inheritdoc */\n selected?: boolean\n\n /** The ID ({@link LLink.id}) of every link using this reroute */\n linkIds: Set<LinkId>\n\n /** The ID ({@link LLink.id}) of every floating link using this reroute */\n floatingLinkIds: Set<LinkId>\n\n /** Cached cos */\n cos: number = 0\n sin: number = 0\n\n /** Bezier curve control point for the \"target\" (input) side of the link */\n controlPoint: Point = [0, 0]\n\n /** @inheritdoc */\n path?: Path2D\n /** @inheritdoc */\n _centreAngle?: number\n /** @inheritdoc */\n _pos: Point = [0, 0]\n\n /** @inheritdoc */\n _dragging?: boolean\n\n /** Colour of the first link that rendered this reroute */\n _colour?: CanvasColour\n\n /** Colour of the first link that rendered this reroute */\n get colour(): CanvasColour {\n return this._colour ?? '#18184d'\n }\n\n /**\n * Used to ensure reroute angles are only executed once per frame.\n * @todo Calculate on change instead.\n */\n private lastRenderTime: number = -Infinity\n\n private readonly inputSlot = new RerouteSlot(this, true)\n private readonly outputSlot = new RerouteSlot(this, false)\n\n get isSlotHovered(): boolean {\n return this.isInputHovered || this.isOutputHovered\n }\n\n get isInputHovered(): boolean {\n return this.inputSlot.hovering\n }\n\n get isOutputHovered(): boolean {\n return this.outputSlot.hovering\n }\n\n get firstLink(): LLink | undefined {\n const linkId = this.linkIds.values().next().value\n return linkId === undefined\n ? undefined\n : this.network.deref()?.links.get(linkId)\n }\n\n get firstFloatingLink(): LLink | undefined {\n const linkId = this.floatingLinkIds.values().next().value\n return linkId === undefined\n ? undefined\n : this.network.deref()?.floatingLinks.get(linkId)\n }\n\n /** @inheritdoc */\n get origin_id(): NodeId | undefined {\n return this.firstLink?.origin_id\n }\n\n /** @inheritdoc */\n get origin_slot(): number | undefined {\n return this.firstLink?.origin_slot\n }\n\n /**\n * Initialises a new link reroute object.\n * @param id Unique identifier for this reroute\n * @param network The network of links this reroute belongs to. Internally converted to a WeakRef.\n * @param pos Position in graph coordinates\n * @param linkIds Link IDs ({@link LLink.id}) of all links that use this reroute\n */\n constructor(\n public readonly id: RerouteId,\n network: LinkNetwork,\n pos?: Point,\n parentId?: RerouteId,\n linkIds?: Iterable<LinkId>,\n floatingLinkIds?: Iterable<LinkId>\n ) {\n this.network = new WeakRef(network)\n this.parentId = parentId\n if (pos) this.pos = pos\n this.linkIds = new Set(linkIds)\n this.floatingLinkIds = new Set(floatingLinkIds)\n }\n\n /**\n * Applies a new parentId to the reroute, and optinoally a new position and linkId.\n * Primarily used for deserialisation.\n * @param parentId The ID of the reroute prior to this reroute, or\n * `undefined` if it is the first reroute connected to a nodes output\n * @param pos The position of this reroute\n * @param linkIds All link IDs that pass through this reroute\n */\n update(\n parentId: RerouteId | undefined,\n pos?: Point,\n linkIds?: Iterable<LinkId>,\n floating?: FloatingRerouteSlot\n ): void {\n this.parentId = parentId\n if (pos) this.pos = pos\n if (linkIds) this.linkIds = new Set(linkIds)\n this.floating = floating\n }\n\n /**\n * Validates the linkIds this reroute has. Removes broken links.\n * @param links Collection of valid links\n * @returns true if any links remain after validation\n */\n validateLinks(\n links: ReadonlyMap<LinkId, LLink>,\n floatingLinks: ReadonlyMap<LinkId, LLink>\n ): boolean {\n const { linkIds, floatingLinkIds } = this\n for (const linkId of linkIds) {\n if (!links.has(linkId)) linkIds.delete(linkId)\n }\n for (const linkId of floatingLinkIds) {\n if (!floatingLinks.has(linkId)) floatingLinkIds.delete(linkId)\n }\n return linkIds.size > 0 || floatingLinkIds.size > 0\n }\n\n /**\n * Retrieves an ordered array of all reroutes from the node output.\n * @param visited Internal. A set of reroutes that this function\n * has already visited whilst recursing up the chain.\n * @returns An ordered array of all reroutes from the node output to this reroute, inclusive.\n * `null` if an infinite loop is detected.\n * `undefined` if the reroute chain or {@link LinkNetwork} are invalid.\n */\n getReroutes(visited = new Set<Reroute>()): Reroute[] | null {\n // No parentId - last in the chain\n if (this.parentIdInternal === undefined) return [this]\n // Invalid chain - looped\n if (visited.has(this)) return null\n visited.add(this)\n\n const parent = this.network.deref()?.reroutes.get(this.parentIdInternal)\n // Invalid parent (or network) - drop silently to recover\n if (!parent) {\n this.parentIdInternal = undefined\n return [this]\n }\n\n const reroutes = parent.getReroutes(visited)\n reroutes?.push(this)\n return reroutes\n }\n\n /**\n * Internal. Called by {@link LLink.findNextReroute}. Not intended for use by itself.\n * @param withParentId The rerouteId to look for\n * @param visited A set of reroutes that have already been visited\n * @returns The reroute that was found, `undefined` if no reroute was found, or `null` if an infinite loop was detected.\n */\n findNextReroute(\n withParentId: RerouteId,\n visited = new Set<Reroute>()\n ): Reroute | null | undefined {\n if (this.parentIdInternal === withParentId) return this\n if (visited.has(this)) return null\n visited.add(this)\n if (this.parentIdInternal === undefined) return\n\n return this.network\n .deref()\n ?.reroutes.get(this.parentIdInternal)\n ?.findNextReroute(withParentId, visited)\n }\n\n /**\n * Finds the output node and output slot of the first link passing through this reroute.\n * @returns The output node and output slot of the first link passing through this reroute, or `undefined` if no link is found.\n */\n findSourceOutput():\n | { node: LGraphNode; output: INodeOutputSlot }\n | undefined {\n const link = this.firstLink ?? this.firstFloatingLink\n if (!link) return\n\n const node = this.network.deref()?.getNodeById(link.origin_id)\n if (!node) return\n\n return {\n node,\n output: node.outputs[link.origin_slot]\n }\n }\n\n /**\n * Finds the inputs and nodes of (floating) links passing through this reroute.\n * @returns An array of objects containing the node and input slot of each link passing through this reroute.\n */\n findTargetInputs():\n | { node: LGraphNode; input: INodeInputSlot; link: LLink }[]\n | undefined {\n const network = this.network.deref()\n if (!network) return\n\n const results: {\n node: LGraphNode\n input: INodeInputSlot\n link: LLink\n }[] = []\n\n addAllResults(network, this.linkIds, network.links)\n addAllResults(network, this.floatingLinkIds, network.floatingLinks)\n\n return results\n\n function addAllResults(\n network: ReadonlyLinkNetwork,\n linkIds: Iterable<LinkId>,\n links: ReadonlyMap<LinkId, LLink>\n ) {\n for (const linkId of linkIds) {\n const link = links.get(linkId)\n if (!link) continue\n\n const node = network.getNodeById(link.target_id)\n const input = node?.inputs[link.target_slot]\n if (!input) continue\n\n results.push({ node, input, link })\n }\n }\n }\n\n /**\n * Retrieves all floating links passing through this reroute.\n * @param from Filters the links by the currently connected link side.\n * @returns An array of floating links\n */\n getFloatingLinks(from: 'input' | 'output'): LLink[] | undefined {\n const floatingLinks = this.network.deref()?.floatingLinks\n if (!floatingLinks) return\n\n const idProp = from === 'input' ? 'origin_id' : 'target_id'\n const out: LLink[] = []\n\n for (const linkId of this.floatingLinkIds) {\n const link = floatingLinks.get(linkId)\n if (link?.[idProp] === -1) out.push(link)\n }\n return out\n }\n\n /**\n * Changes the origin node/output of all floating links that pass through this reroute.\n * @param node The new origin node\n * @param output The new origin output slot\n * @param index The slot index of {@link output}\n */\n setFloatingLinkOrigin(\n node: LGraphNode,\n output: INodeOutputSlot,\n index: number\n ) {\n const network = this.network.deref()\n const floatingOutLinks = this.getFloatingLinks('output')\n if (!floatingOutLinks)\n throw new Error('[setFloatingLinkOrigin]: Invalid network.')\n if (!floatingOutLinks.length) return\n\n output._floatingLinks ??= new Set()\n\n for (const link of floatingOutLinks) {\n // Update cached floating links\n output._floatingLinks.add(link)\n\n network\n ?.getNodeById(link.origin_id)\n ?.outputs[link.origin_slot]?._floatingLinks?.delete(link)\n\n // Update the floating link\n link.origin_id = node.id\n link.origin_slot = index\n }\n }\n\n /** @inheritdoc */\n move(deltaX: number, deltaY: number) {\n const previousPos = { x: this.posInternal[0], y: this.posInternal[1] }\n this.posInternal[0] += deltaX\n this.posInternal[1] += deltaY\n\n // Update Layout Store with new position\n layoutMutations.setSource(LayoutSource.Canvas)\n layoutMutations.moveReroute(\n this.id,\n { x: this.posInternal[0], y: this.posInternal[1] },\n previousPos\n )\n }\n\n /** @inheritdoc */\n snapToGrid(snapTo: number): boolean {\n if (!snapTo) return false\n\n const { pos } = this\n pos[0] = snapTo * Math.round(pos[0] / snapTo)\n pos[1] = snapTo * Math.round(pos[1] / snapTo)\n return true\n }\n\n removeAllFloatingLinks() {\n for (const linkId of this.floatingLinkIds) {\n this.removeFloatingLink(linkId)\n }\n }\n\n removeFloatingLink(linkId: LinkId) {\n const network = this.network.deref()\n if (!network) return\n\n const floatingLink = network.floatingLinks.get(linkId)\n if (!floatingLink) {\n console.warn(\n `[Reroute.removeFloatingLink] Floating link not found: ${linkId}, ignoring and discarding ID.`\n )\n this.floatingLinkIds.delete(linkId)\n return\n }\n\n network.removeFloatingLink(floatingLink)\n }\n\n /**\n * Removes a link or floating link from this reroute, by matching link object instance equality.\n * @param link The link to remove.\n * @remarks Does not remove the link from the network.\n */\n removeLink(link: LLink) {\n const network = this.network.deref()\n if (!network) return\n\n const floatingLink = network.floatingLinks.get(link.id)\n if (link === floatingLink) {\n this.floatingLinkIds.delete(link.id)\n } else {\n this.linkIds.delete(link.id)\n }\n }\n\n remove() {\n const network = this.network.deref()\n if (!network) return\n\n network.removeReroute(this.id)\n }\n\n calculateAngle(\n lastRenderTime: number,\n network: ReadonlyLinkNetwork,\n linkStart: Point\n ): void {\n // Ensure we run once per render\n if (!(lastRenderTime > this.lastRenderTime)) return\n this.lastRenderTime = lastRenderTime\n\n const { id, pos: thisPos } = this\n\n // Add all link angles\n const angles: number[] = []\n let sum = 0\n calculateAngles(this.linkIds, network.links)\n calculateAngles(this.floatingLinkIds, network.floatingLinks)\n\n // Invalid - reset\n if (!angles.length) {\n this.cos = 0\n this.sin = 0\n this.controlPoint[0] = 0\n this.controlPoint[1] = 0\n return\n }\n\n sum /= angles.length\n\n const originToReroute = Math.atan2(\n this.posInternal[1] - linkStart[1],\n this.posInternal[0] - linkStart[0]\n )\n let diff = (originToReroute - sum) * 0.5\n if (Math.abs(diff) > Math.PI * 0.5) diff += Math.PI\n const dist = Math.min(\n Reroute.maxSplineOffset,\n distance(linkStart, this.posInternal) * 0.25\n )\n\n // Store results\n const originDiff = originToReroute - diff\n const cos = Math.cos(originDiff)\n const sin = Math.sin(originDiff)\n\n this.cos = cos\n this.sin = sin\n this.controlPoint[0] = dist * -cos\n this.controlPoint[1] = dist * -sin\n\n /**\n * Calculates the direction of each link and adds it to the array.\n * @param linkIds The IDs of the links to calculate\n * @param links The link container from the link network.\n */\n function calculateAngles(\n linkIds: Iterable<LinkId>,\n links: ReadonlyMap<LinkId, LLink>\n ) {\n for (const linkId of linkIds) {\n const link = links.get(linkId)\n const pos = getNextPos(network, link, id)\n if (!pos) continue\n\n const angle = getDirection(thisPos, pos)\n angles.push(angle)\n sum += angle\n }\n }\n }\n\n /**\n * Renders the reroute on the canvas.\n * @param ctx Canvas context to draw on\n * @param backgroundPattern The canvas background pattern; used to make floating reroutes appear washed out.\n * @remarks Leaves {@link ctx}.fillStyle, strokeStyle, and lineWidth dirty (perf.).\n */\n draw(ctx: CanvasRenderingContext2D, backgroundPattern?: CanvasPattern): void {\n const { globalAlpha } = ctx\n const { pos } = this\n\n ctx.beginPath()\n ctx.arc(pos[0], pos[1], Reroute.radius, 0, 2 * Math.PI)\n\n if (this.linkIds.size === 0) {\n ctx.fillStyle = backgroundPattern ?? '#797979'\n ctx.fill()\n ctx.globalAlpha = globalAlpha * 0.33\n }\n\n ctx.fillStyle = this.colour\n ctx.lineWidth = Reroute.radius * 0.1\n ctx.strokeStyle = 'rgb(0,0,0,0.5)'\n ctx.fill()\n ctx.stroke()\n\n ctx.fillStyle = '#ffffff55'\n ctx.strokeStyle = 'rgb(0,0,0,0.3)'\n ctx.beginPath()\n ctx.arc(pos[0], pos[1], Reroute.radius * 0.8, 0, 2 * Math.PI)\n ctx.fill()\n ctx.stroke()\n\n if (this.selected) {\n ctx.strokeStyle = '#fff'\n ctx.beginPath()\n ctx.arc(pos[0], pos[1], Reroute.radius * 1.2, 0, 2 * Math.PI)\n ctx.stroke()\n }\n\n if (Reroute.drawIdBadge) {\n const idBadge = new LGraphBadge({ text: this.id.toString() })\n const x = pos[0] - idBadge.getWidth(ctx) * 0.5\n const y = pos[1] - idBadge.height - Reroute.radius - 2\n idBadge.draw(ctx, x, y)\n }\n\n ctx.globalAlpha = globalAlpha\n }\n\n /**\n * Draws the input and output slots on the canvas, if the slots are visible.\n * @param ctx The canvas context to draw on.\n */\n drawSlots(ctx: CanvasRenderingContext2D): void {\n this.inputSlot.draw(ctx)\n this.outputSlot.draw(ctx)\n }\n\n drawHighlight(ctx: CanvasRenderingContext2D, colour: CanvasColour): void {\n const { pos } = this\n\n const { strokeStyle, lineWidth } = ctx\n ctx.strokeStyle = colour\n ctx.lineWidth = 1\n\n ctx.beginPath()\n ctx.arc(pos[0], pos[1], Reroute.radius * 1.5, 0, 2 * Math.PI)\n ctx.stroke()\n\n ctx.strokeStyle = strokeStyle\n ctx.lineWidth = lineWidth\n }\n\n /**\n * Updates visibility of the input and output slots, based on the position of the pointer.\n * @param pos The position of the pointer.\n * @returns `true` if any changes require a redraw.\n */\n updateVisibility(pos: Point): boolean {\n const input = this.inputSlot\n const output = this.outputSlot\n input.dirty = false\n output.dirty = false\n\n const { firstFloatingLink } = this\n const hasLink = !!this.firstLink\n\n const showInput = hasLink || firstFloatingLink?.isFloatingOutput\n const showOutput = hasLink || firstFloatingLink?.isFloatingInput\n const showEither = showInput || showOutput\n\n // Check if even in the vicinity\n if (showEither && isPointInRect(pos, this.hoverArea)) {\n const outlineOnly = this.contains(pos)\n\n if (showInput) input.update(pos, outlineOnly)\n if (showOutput) output.update(pos, outlineOnly)\n } else {\n this.hideSlots()\n }\n\n return input.dirty || output.dirty\n }\n\n /** Prevents rendering of the input and output slots. */\n hideSlots() {\n this.inputSlot.hide()\n this.outputSlot.hide()\n }\n\n /**\n * Precisely determines if {@link pos} is inside this reroute.\n * @param pos The position to check (canvas space)\n * @returns `true` if {@link pos} is within the reroute's radius.\n */\n containsPoint(pos: Point): boolean {\n return isPointInRect(pos, this.hoverArea) && this.contains(pos)\n }\n\n private contains(pos: Point): boolean {\n return distance(this.pos, pos) <= Reroute.radius\n }\n\n /** @inheritdoc */\n asSerialisable(): SerialisableReroute {\n const { id, parentId, pos, linkIds } = this\n return {\n id,\n parentId,\n pos: [pos[0], pos[1]],\n linkIds: [...linkIds],\n floating: this.floating ? { slotType: this.floating.slotType } : undefined\n }\n }\n}\n\n/**\n * Represents a slot on a reroute.\n * @private Designed for internal use within this module.\n */\nclass RerouteSlot {\n /** The reroute that the slot belongs to. */\n private readonly reroute: Reroute\n\n private readonly offsetMultiplier: 1 | -1\n /** Centre point of this slot. */\n get pos(): Point {\n const [x, y] = this.reroute.pos\n return [x + Reroute.slotOffset * this.offsetMultiplier, y]\n }\n\n /** Whether any changes require a redraw. */\n dirty: boolean = false\n\n private hoveringInternal = false\n /** Whether the pointer is hovering over the slot itself. */\n get hovering() {\n return this.hoveringInternal\n }\n\n set hovering(value) {\n if (!Object.is(this.hoveringInternal, value)) {\n this.hoveringInternal = value\n this.dirty = true\n }\n }\n\n private showOutlineInternal = false\n /** Whether the slot outline / faint background is visible. */\n get showOutline() {\n return this.showOutlineInternal\n }\n\n set showOutline(value) {\n if (!Object.is(this.showOutlineInternal, value)) {\n this.showOutlineInternal = value\n this.dirty = true\n }\n }\n\n constructor(reroute: Reroute, isInput: boolean) {\n this.reroute = reroute\n this.offsetMultiplier = isInput ? -1 : 1\n }\n\n /**\n * Updates the slot's visibility based on the position of the pointer.\n * @param pos The position of the pointer.\n * @param outlineOnly If `true`, slot will display with the faded outline only ({@link showOutline}).\n */\n update(pos: Point, outlineOnly?: boolean) {\n if (outlineOnly) {\n this.hovering = false\n this.showOutline = true\n } else {\n const dist = distance(this.pos, pos)\n this.hovering = dist <= 2 * Reroute.slotRadius\n this.showOutline = dist <= 5 * Reroute.slotRadius\n }\n }\n\n /** Hides the slot. */\n hide() {\n this.hovering = false\n this.showOutline = false\n }\n\n /**\n * Draws the slot on the canvas.\n * @param ctx The canvas context to draw on.\n */\n draw(ctx: CanvasRenderingContext2D): void {\n const { fillStyle, strokeStyle, lineWidth } = ctx\n const {\n showOutline,\n hovering,\n pos: [x, y]\n } = this\n if (!showOutline) return\n\n try {\n ctx.fillStyle = hovering ? this.reroute.colour : 'rgba(127,127,127,0.3)'\n ctx.strokeStyle = 'rgb(0,0,0,0.5)'\n ctx.lineWidth = 1\n\n ctx.beginPath()\n ctx.arc(x, y, Reroute.slotRadius, 0, 2 * Math.PI)\n ctx.fill()\n ctx.stroke()\n } finally {\n ctx.fillStyle = fillStyle\n ctx.strokeStyle = strokeStyle\n ctx.lineWidth = lineWidth\n }\n }\n}\n\n/**\n * Retrieves the position of the next reroute in the chain, or the destination input slot on this link.\n * @param network The network of links\n * @param link The link representing the current reroute chain\n * @param id The ID of \"this\" reroute\n * @returns The position of the next reroute or the input slot target, otherwise `undefined`.\n */\nfunction getNextPos(\n network: ReadonlyLinkNetwork,\n link: LLink | undefined,\n id: RerouteId\n) {\n if (!link) return\n\n const linkPos = LLink.findNextReroute(network, link, id)?.pos\n if (linkPos) return linkPos\n\n // Floating link with no input to find\n if (link.target_id === -1 || link.target_slot === -1) return\n\n return network.getNodeById(link.target_id)?.getInputPos(link.target_slot)\n}\n\n/** Returns the direction from one point to another in radians. */\nfunction getDirection(fromPos: Point, toPos: Point) {\n return Math.atan2(toPos[1] - fromPos[1], toPos[0] - fromPos[0])\n}\n","import type { ISlotType } from './litegraph'\n\nexport function parseSlotTypes(type: ISlotType): string[] {\n return type == '' || type == '0'\n ? ['*']\n : String(type).toLowerCase().split(',')\n}\n\n/**\n * Creates a unique name by appending an underscore and a number to the end of the name\n * if it already exists.\n * @param name The name to make unique\n * @param existingNames The names that already exist. Default: an empty array\n * @returns The name, or a unique name if it already exists.\n */\nexport function nextUniqueName(\n name: string,\n existingNames: string[] = []\n): string {\n let i = 1\n const baseName = name\n while (existingNames.includes(name)) {\n name = `${baseName}_${i++}`\n }\n return name\n}\n","import { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { parseSlotTypes } from '@/lib/litegraph/src/strings'\n\nimport type { ISlotType, Positionable } from '../interfaces'\n\n/**\n * Creates a flat set of all positionable items by recursively iterating through all child items.\n *\n * Does not include or recurse into pinned items.\n * @param items The original set of items to iterate through\n * @returns All unpinned items in the original set, and recursively, their children\n */\nexport function getAllNestedItems(\n items: ReadonlySet<Positionable>\n): Set<Positionable> {\n const allItems = new Set<Positionable>()\n if (items) {\n for (const item of items) addRecursively(item, allItems)\n }\n return allItems\n\n function addRecursively(\n item: Positionable,\n flatSet: Set<Positionable>\n ): void {\n if (flatSet.has(item) || item.pinned) return\n flatSet.add(item)\n if (item.children) {\n for (const child of item.children) addRecursively(child, flatSet)\n }\n }\n}\n\n/**\n * Iterates through a collection of {@link Positionable} items, returning the first {@link LGraphNode}.\n * @param items The items to search through\n * @returns The first node found in {@link items}, otherwise `undefined`\n */\nexport function findFirstNode(\n items: Iterable<Positionable>\n): LGraphNode | undefined {\n for (const item of items) {\n if (item instanceof LGraphNode) return item\n }\n}\n\ntype FreeSlotResult<T extends { type: ISlotType }> =\n | { index: number; slot: T }\n | undefined\n\n/**\n * Finds the first free in/out slot with any of the comma-delimited types in {@link type}.\n *\n * If no slots are free, falls back in order to:\n * - The first free wildcard slot\n * - The first occupied slot\n * - The first occupied wildcard slot\n * @param slots The iterable of node slots slots to search through\n * @param type The {@link ISlotType type} of slot to find\n * @param hasNoLinks A predicate that returns `true` if the slot is free.\n * @returns The index and slot if found, otherwise `undefined`.\n */\nexport function findFreeSlotOfType<T extends { type: ISlotType }>(\n slots: T[],\n type: ISlotType,\n hasNoLinks: (slot: T) => boolean\n) {\n if (!slots?.length) return\n\n let occupiedSlot: FreeSlotResult<T>\n let wildSlot: FreeSlotResult<T>\n let occupiedWildSlot: FreeSlotResult<T>\n\n const validTypes = parseSlotTypes(type)\n\n for (const [index, slot] of slots.entries()) {\n const slotTypes = parseSlotTypes(slot.type)\n\n for (const validType of validTypes) {\n for (const slotType of slotTypes) {\n if (slotType === validType) {\n if (hasNoLinks(slot)) {\n // Exact match - short circuit\n return { index, slot }\n }\n // In case we can't find a free slot.\n occupiedSlot ??= { index, slot }\n } else if (!wildSlot && (validType === '*' || slotType === '*')) {\n // Save the first free wildcard slot as a fallback\n if (hasNoLinks(slot)) {\n wildSlot = { index, slot }\n } else {\n occupiedWildSlot ??= { index, slot }\n }\n }\n }\n }\n }\n return wildSlot ?? occupiedSlot ?? occupiedWildSlot\n}\n","import type { NeverNever, PickNevers } from '@/lib/litegraph/src/types/utility'\n\ntype EventListeners<T> = {\n readonly [K in keyof T]:\n | ((this: EventTarget, ev: CustomEvent<T[K]>) => any)\n | EventListenerObject\n | null\n}\n\n/**\n * Has strongly-typed overrides of {@link EventTarget.addEventListener} and {@link EventTarget.removeEventListener}.\n */\nexport interface ICustomEventTarget<\n EventMap extends Record<Keys, unknown>,\n Keys extends keyof EventMap & string = keyof EventMap & string\n> {\n addEventListener<K extends Keys>(\n type: K,\n listener: EventListeners<EventMap>[K],\n options?: boolean | AddEventListenerOptions\n ): void\n\n removeEventListener<K extends Keys>(\n type: K,\n listener: EventListeners<EventMap>[K],\n options?: boolean | EventListenerOptions\n ): void\n\n /** @deprecated Use {@link dispatch}. */\n dispatchEvent(event: never): boolean\n}\n\n/**\n * Capable of dispatching strongly-typed events via {@link dispatch}.\n * Overloads are used to ensure detail param is correctly optional.\n */\nexport interface CustomEventDispatcher<\n EventMap extends Record<Keys, unknown>,\n Keys extends keyof EventMap & string = keyof EventMap & string\n> {\n dispatch<T extends keyof NeverNever<EventMap>>(\n type: T,\n detail: EventMap[T]\n ): boolean\n dispatch<T extends keyof PickNevers<EventMap>>(type: T): boolean\n}\n\n/**\n * A strongly-typed, custom {@link EventTarget} that can dispatch and listen for events.\n *\n * 1. Define an event map\n * ```ts\n * export interface CustomEventMap {\n * \"my-event\": { message: string }\n * \"simple-event\": never\n * }\n * ```\n *\n * 2. Create an event emitter\n * ```ts\n * // By subclassing\n * class MyClass extends CustomEventTarget<CustomEventMap> {\n * // ...\n * }\n *\n * // Or simply create an instance:\n * const events = new CustomEventTarget<CustomEventMap>()\n * ```\n *\n * 3. Dispatch events\n * ```ts\n * // Extended class\n * const myClass = new MyClass()\n * myClass.dispatch(\"my-event\", { message: \"Hello, world!\" })\n * myClass.dispatch(\"simple-event\")\n *\n * // Instance\n * const events = new CustomEventTarget<CustomEventMap>()\n * events.dispatch(\"my-event\", { message: \"Hello, world!\" })\n * events.dispatch(\"simple-event\")\n * ```\n */\nexport class CustomEventTarget<\n EventMap extends Record<Keys, unknown>,\n Keys extends keyof EventMap & string = keyof EventMap & string\n>\n extends EventTarget\n implements ICustomEventTarget<EventMap, Keys>\n{\n /**\n * Type-safe event dispatching.\n * @see {@link EventTarget.dispatchEvent}\n * @param type Name of the event to dispatch\n * @param detail A custom object to send with the event\n * @returns `true` if the event was dispatched successfully, otherwise `false`.\n */\n dispatch<T extends keyof NeverNever<EventMap>>(\n type: T,\n detail: EventMap[T]\n ): boolean\n dispatch<T extends keyof PickNevers<EventMap>>(type: T): boolean\n dispatch<T extends keyof EventMap>(type: T, detail?: EventMap[T]) {\n const event = new CustomEvent(type as string, { detail, cancelable: true })\n return super.dispatchEvent(event)\n }\n\n override addEventListener<K extends Keys>(\n type: K,\n listener: EventListeners<EventMap>[K],\n options?: boolean | AddEventListenerOptions\n ): void {\n // Assertion: Contravariance on CustomEvent => Event\n super.addEventListener(type as string, listener as EventListener, options)\n }\n\n override removeEventListener<K extends Keys>(\n type: K,\n listener: EventListeners<EventMap>[K],\n options?: boolean | EventListenerOptions\n ): void {\n // Assertion: Contravariance on CustomEvent => Event\n super.removeEventListener(\n type as string,\n listener as EventListener,\n options\n )\n }\n\n /** @deprecated Use {@link dispatch}. */\n override dispatchEvent(event: never): boolean {\n return super.dispatchEvent(event)\n }\n}\n","import { clamp } from 'es-toolkit/compat'\n\nimport type { ReadOnlyRect, Size } from '@/lib/litegraph/src/interfaces'\n\n/**\n * Basic width and height, with min/max constraints.\n *\n * - The {@link width} and {@link height} properties are readonly\n * - Size is set via {@link desiredWidth} and {@link desiredHeight} properties\n * - Width and height are then updated, clamped to min/max values\n */\nexport class ConstrainedSize {\n #width: number = 0\n #height: number = 0\n #desiredWidth: number = 0\n #desiredHeight: number = 0\n\n minWidth: number = 0\n minHeight: number = 0\n maxWidth: number = Infinity\n maxHeight: number = Infinity\n\n get width() {\n return this.#width\n }\n\n get height() {\n return this.#height\n }\n\n get desiredWidth() {\n return this.#desiredWidth\n }\n\n set desiredWidth(value: number) {\n this.#desiredWidth = value\n this.#width = clamp(value, this.minWidth, this.maxWidth)\n }\n\n get desiredHeight() {\n return this.#desiredHeight\n }\n\n set desiredHeight(value: number) {\n this.#desiredHeight = value\n this.#height = clamp(value, this.minHeight, this.maxHeight)\n }\n\n constructor(width: number, height: number) {\n this.desiredWidth = width\n this.desiredHeight = height\n }\n\n static fromSize(size: Readonly<Size>): ConstrainedSize {\n return new ConstrainedSize(size[0], size[1])\n }\n\n static fromRect(rect: ReadOnlyRect): ConstrainedSize {\n return new ConstrainedSize(rect[2], rect[3])\n }\n\n setSize(size: Readonly<Size>): void {\n this.desiredWidth = size[0]\n this.desiredHeight = size[1]\n }\n\n setValues(width: number, height: number): void {\n this.desiredWidth = width\n this.desiredHeight = height\n }\n\n toSize(): Size {\n return [this.#width, this.#height]\n }\n}\n","import { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink, LinkId } from '@/lib/litegraph/src/LLink'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport { SlotShape } from '@/lib/litegraph/src/draw'\nimport { ConstrainedSize } from '@/lib/litegraph/src/infrastructure/ConstrainedSize'\nimport { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'\nimport type {\n DefaultConnectionColors,\n Hoverable,\n INodeInputSlot,\n INodeOutputSlot,\n Point,\n ReadOnlyRect,\n Size\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport { SlotBase } from '@/lib/litegraph/src/node/SlotBase'\nimport type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'\nimport type {\n Serialisable,\n SubgraphIO\n} from '@/lib/litegraph/src/types/serialisation'\nimport { createUuidv4 } from '@/lib/litegraph/src/utils/uuid'\nimport type { UUID } from '@/lib/litegraph/src/utils/uuid'\n\nimport type { SubgraphInput } from './SubgraphInput'\nimport type { SubgraphInputNode } from './SubgraphInputNode'\nimport type { SubgraphOutput } from './SubgraphOutput'\nimport type { SubgraphOutputNode } from './SubgraphOutputNode'\n\ninterface SubgraphSlotDrawOptions {\n ctx: CanvasRenderingContext2D\n colorContext: DefaultConnectionColors\n lowQuality?: boolean\n fromSlot?: INodeInputSlot | INodeOutputSlot | SubgraphInput | SubgraphOutput\n editorAlpha?: number\n}\n\n/** Shared base class for the slots used on Subgraph . */\nexport abstract class SubgraphSlot\n extends SlotBase\n implements SubgraphIO, Hoverable, Serialisable<SubgraphIO>\n{\n static get defaultHeight() {\n return LiteGraph.NODE_SLOT_HEIGHT\n }\n\n readonly #pos: Point = [0, 0]\n\n readonly measurement: ConstrainedSize = new ConstrainedSize(\n SubgraphSlot.defaultHeight,\n SubgraphSlot.defaultHeight\n )\n\n readonly id: UUID\n readonly parent: SubgraphInputNode | SubgraphOutputNode\n override type: string\n\n readonly linkIds: LinkId[] = []\n\n override readonly boundingRect: Rectangle = new Rectangle(\n 0,\n 0,\n 0,\n SubgraphSlot.defaultHeight\n )\n\n override get pos() {\n return this.#pos\n }\n\n override set pos(value) {\n if (!value || value.length < 2) return\n\n this.#pos[0] = value[0]\n this.#pos[1] = value[1]\n }\n\n /** Whether this slot is connected to another slot. */\n override get isConnected() {\n return this.linkIds.length > 0\n }\n\n /** The display name of this slot. */\n get displayName() {\n return this.label ?? this.localized_name ?? this.name\n }\n\n abstract get labelPos(): Point\n\n constructor(\n slot: SubgraphIO,\n parent: SubgraphInputNode | SubgraphOutputNode\n ) {\n super(slot.name, slot.type)\n\n Object.assign(this, slot)\n this.id = slot.id ?? createUuidv4()\n this.type = slot.type\n this.parent = parent\n }\n\n isPointerOver: boolean = false\n\n containsPoint(point: Point): boolean {\n return this.boundingRect.containsPoint(point)\n }\n\n onPointerMove(e: CanvasPointerEvent): void {\n this.isPointerOver = this.boundingRect.containsXy(e.canvasX, e.canvasY)\n }\n\n getLinks(): LLink[] {\n const links: LLink[] = []\n const { subgraph } = this.parent\n\n for (const id of this.linkIds) {\n const link = subgraph.getLink(id)\n if (link) links.push(link)\n }\n return links\n }\n\n decrementSlots(inputsOrOutputs: 'inputs' | 'outputs'): void {\n const { links } = this.parent.subgraph\n const linkProperty =\n inputsOrOutputs === 'inputs' ? 'origin_slot' : 'target_slot'\n\n for (const linkId of this.linkIds) {\n const link = links.get(linkId)\n if (link) link[linkProperty]--\n else console.warn('decrementSlots: link ID not found', linkId)\n }\n }\n\n measure(): Readonly<Size> {\n const width = LGraphCanvas._measureText?.(this.displayName) ?? 0\n\n const { defaultHeight } = SubgraphSlot\n this.measurement.setValues(width + defaultHeight, defaultHeight)\n return this.measurement.toSize()\n }\n\n abstract arrange(rect: ReadOnlyRect): void\n\n abstract connect(\n slot: INodeInputSlot | INodeOutputSlot,\n node: LGraphNode,\n afterRerouteId?: RerouteId\n ): LLink | undefined\n\n /**\n * Disconnects all links connected to this slot.\n */\n disconnect(): void {\n const { subgraph } = this.parent\n\n for (const linkId of this.linkIds) {\n subgraph.removeLink(linkId)\n }\n\n this.linkIds.length = 0\n }\n\n /**\n * Checks if this slot is a valid target for a connection from the given slot.\n * @param fromSlot The slot that is being dragged to connect to this slot.\n * @returns true if the connection is valid, false otherwise.\n */\n abstract isValidTarget(\n fromSlot: INodeInputSlot | INodeOutputSlot | SubgraphInput | SubgraphOutput\n ): boolean\n\n /** @remarks Leaves the context dirty. */\n draw({\n ctx,\n colorContext,\n lowQuality,\n fromSlot,\n editorAlpha = 1\n }: SubgraphSlotDrawOptions): void {\n // Assertion: SlotShape is a subset of RenderShape\n const shape = this.shape as unknown as SlotShape\n const {\n isPointerOver,\n pos: [x, y]\n } = this\n\n // Check if this slot is a valid target for the current dragging connection\n const isValidTarget = fromSlot ? this.isValidTarget(fromSlot) : true\n const isValid = !fromSlot || isValidTarget\n\n // Only highlight if the slot is valid AND mouse is over it\n const highlight = isValid && isPointerOver\n\n // Save current alpha\n const previousAlpha = ctx.globalAlpha\n\n // Set opacity based on validity when dragging a connection\n ctx.globalAlpha = isValid ? editorAlpha : 0.4 * editorAlpha\n\n ctx.beginPath()\n\n // Default rendering for circle, hollow circle.\n const color = this.renderingColor(colorContext)\n if (lowQuality) {\n ctx.fillStyle = color\n\n ctx.rect(x - 4, y - 4, 8, 8)\n ctx.fill()\n } else if (shape === SlotShape.HollowCircle) {\n ctx.lineWidth = 3\n ctx.strokeStyle = color\n\n const radius = highlight ? 4 : 3\n ctx.arc(x, y, radius, 0, Math.PI * 2)\n ctx.stroke()\n } else {\n // Normal circle\n ctx.fillStyle = color\n\n const radius = highlight ? 5 : 4\n ctx.arc(x, y, radius, 0, Math.PI * 2)\n ctx.fill()\n }\n\n // Draw label with current opacity\n if (this.displayName) {\n const [labelX, labelY] = this.labelPos\n // Also apply highlight logic to text color\n ctx.fillStyle = highlight ? 'white' : LiteGraph.NODE_TEXT_COLOR || '#AAA'\n ctx.fillText(this.displayName, labelX, labelY)\n }\n\n // Restore alpha\n ctx.globalAlpha = previousAlpha\n }\n\n asSerialisable(): SubgraphIO {\n const {\n id,\n name,\n type,\n linkIds,\n localized_name,\n label,\n dir,\n shape,\n color_off,\n color_on,\n pos\n } = this\n return {\n id,\n name,\n type,\n linkIds,\n localized_name,\n label,\n dir,\n shape,\n color_off,\n color_on,\n pos\n }\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { LLink } from '@/lib/litegraph/src/LLink'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { SubgraphInputEventMap } from '@/lib/litegraph/src/infrastructure/SubgraphInputEventMap'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n Point,\n ReadOnlyRect\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'\nimport type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport type { SubgraphInputNode } from './SubgraphInputNode'\nimport type { SubgraphOutput } from './SubgraphOutput'\nimport { SubgraphSlot } from './SubgraphSlotBase'\nimport { isNodeSlot, isSubgraphOutput } from './subgraphUtils'\n\n/**\n * An input \"slot\" from a parent graph into a subgraph.\n *\n * IMPORTANT: A subgraph \"input\" is both an input AND an output. It creates an extra link connection point between\n * a parent graph and a subgraph, so is conceptually similar to a reroute.\n *\n * This can be a little confusing, but is easier to visualise when imagining editing a subgraph.\n * You have \"Subgraph Inputs\", because they are coming into the subgraph, which then connect to \"node inputs\".\n *\n * Functionally, however, when editing a subgraph, that \"subgraph input\" is the \"origin\" or \"output side\" of a link.\n */\nexport class SubgraphInput extends SubgraphSlot {\n declare parent: SubgraphInputNode\n\n events = new CustomEventTarget<SubgraphInputEventMap>()\n\n /** The linked widget that this slot is connected to. */\n #widgetRef?: WeakRef<IBaseWidget>\n\n get _widget() {\n return this.#widgetRef?.deref()\n }\n\n set _widget(widget) {\n this.#widgetRef = widget ? new WeakRef(widget) : undefined\n }\n\n override connect(\n slot: INodeInputSlot,\n node: LGraphNode,\n afterRerouteId?: RerouteId\n ): LLink | undefined {\n const { subgraph } = this.parent\n\n // Allow nodes to block connection\n const inputIndex = node.inputs.indexOf(slot)\n if (\n node.onConnectInput?.(inputIndex, this.type, this, this.parent, -1) ===\n false\n )\n return\n\n // if (slot instanceof SubgraphOutput) {\n // // Subgraph IO nodes have no special handling at present.\n // return new LLink(\n // ++subgraph.state.lastLinkId,\n // this.type,\n // this.parent.id,\n // this.parent.slots.indexOf(this),\n // node.id,\n // inputIndex,\n // afterRerouteId,\n // )\n // }\n\n // Disconnect target input, if it is already connected.\n if (slot.link != null) {\n subgraph.beforeChange()\n const link = subgraph.getLink(slot.link)\n this.parent._disconnectNodeInput(node, slot, link)\n }\n\n const inputWidget = node.getWidgetFromSlot(slot)\n if (inputWidget) {\n if (!this.matchesWidget(inputWidget)) {\n console.warn('Target input has invalid widget.', slot, node)\n return\n }\n\n this._widget ??= inputWidget\n this.events.dispatch('input-connected', {\n input: slot,\n widget: inputWidget\n })\n }\n\n const link = new LLink(\n ++subgraph.state.lastLinkId,\n slot.type,\n this.parent.id,\n this.parent.slots.indexOf(this),\n node.id,\n inputIndex,\n afterRerouteId\n )\n\n // Add to graph links list\n subgraph._links.set(link.id, link)\n\n // Set link ID in each slot\n this.linkIds.push(link.id)\n slot.link = link.id\n\n // Reroutes\n const reroutes = LLink.getReroutes(subgraph, link)\n for (const reroute of reroutes) {\n reroute.linkIds.add(link.id)\n if (reroute.floating) delete reroute.floating\n reroute._dragging = undefined\n }\n\n // If this is the terminus of a floating link, remove it\n const lastReroute = reroutes.at(-1)\n if (lastReroute) {\n for (const linkId of lastReroute.floatingLinkIds) {\n const link = subgraph.floatingLinks.get(linkId)\n if (link?.parentId === lastReroute.id) {\n subgraph.removeFloatingLink(link)\n }\n }\n }\n subgraph._version++\n\n node.onConnectionsChange?.(NodeSlotType.INPUT, inputIndex, true, link, slot)\n\n subgraph.afterChange()\n\n return link\n }\n\n get labelPos(): Point {\n const [x, y, , height] = this.boundingRect\n return [x, y + height * 0.5]\n }\n\n getConnectedWidgets(): IBaseWidget[] {\n const { subgraph } = this.parent\n const widgets: IBaseWidget[] = []\n\n for (const linkId of this.linkIds) {\n const link = subgraph.getLink(linkId)\n if (!link) {\n console.error('Link not found', linkId)\n continue\n }\n\n const resolved = link.resolve(subgraph)\n if (resolved.input && resolved.inputNode?.widgets) {\n // Has no widget\n const widgetNamePojo = resolved.input.widget\n if (!widgetNamePojo) continue\n\n // Invalid widget name\n if (!widgetNamePojo.name) {\n console.warn('Invalid widget name', widgetNamePojo)\n continue\n }\n\n const widget = resolved.inputNode.widgets.find(\n (w) => w.name === widgetNamePojo.name\n )\n if (!widget) {\n console.warn('Widget not found', widgetNamePojo)\n continue\n }\n\n widgets.push(widget)\n }\n }\n return widgets\n }\n\n /**\n * Validates that the connection between the new slot and the existing widget is valid.\n * Used to prevent connections between widgets that are not of the same type.\n * @param otherWidget The widget to compare to.\n * @returns `true` if the connection is valid, otherwise `false`.\n */\n matchesWidget(otherWidget: IBaseWidget): boolean {\n const widget = this.#widgetRef?.deref()\n if (!widget) return true\n\n if (\n otherWidget.type !== widget.type ||\n otherWidget.options.min !== widget.options.min ||\n otherWidget.options.max !== widget.options.max ||\n otherWidget.options.step !== widget.options.step ||\n otherWidget.options.step2 !== widget.options.step2 ||\n otherWidget.options.precision !== widget.options.precision\n ) {\n return false\n }\n\n return true\n }\n\n override disconnect(): void {\n super.disconnect()\n\n this.events.dispatch('input-disconnected', { input: this })\n }\n\n /** For inputs, x is the right edge of the input node. */\n override arrange(rect: ReadOnlyRect): void {\n const [right, top, width, height] = rect\n const { boundingRect: b, pos } = this\n\n b[0] = right - width\n b[1] = top\n b[2] = width\n b[3] = height\n\n pos[0] = right - height * 0.5\n pos[1] = top + height * 0.5\n }\n\n /**\n * Checks if this slot is a valid target for a connection from the given slot.\n * For SubgraphInput (which acts as an output inside the subgraph),\n * the fromSlot should be an input slot.\n */\n override isValidTarget(\n fromSlot: INodeInputSlot | INodeOutputSlot | SubgraphInput | SubgraphOutput\n ): boolean {\n if (isNodeSlot(fromSlot)) {\n return (\n 'link' in fromSlot &&\n LiteGraph.isValidConnection(this.type, fromSlot.type)\n )\n }\n\n if (isSubgraphOutput(fromSlot)) {\n return LiteGraph.isValidConnection(this.type, fromSlot.type)\n }\n\n return false\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport type { INodeInputSlot, Point } from '@/lib/litegraph/src/interfaces'\nimport { nextUniqueName } from '@/lib/litegraph/src/strings'\nimport { zeroUuid } from '@/lib/litegraph/src/utils/uuid'\n\nimport { SubgraphInput } from './SubgraphInput'\nimport type { SubgraphInputNode } from './SubgraphInputNode'\n\n/**\n * A virtual slot that simply creates a new input slot when connected to.\n */\nexport class EmptySubgraphInput extends SubgraphInput {\n declare parent: SubgraphInputNode\n\n constructor(parent: SubgraphInputNode) {\n super(\n {\n id: zeroUuid,\n name: '',\n type: ''\n },\n parent\n )\n }\n\n override connect(\n slot: INodeInputSlot,\n node: LGraphNode,\n afterRerouteId?: RerouteId\n ): LLink | undefined {\n const { subgraph } = this.parent\n const existingNames = subgraph.inputs.map((x) => x.name)\n\n const name = nextUniqueName(slot.name, existingNames)\n const input = subgraph.addInput(name, String(slot.type))\n return input.connect(slot, node, afterRerouteId)\n }\n\n override get labelPos(): Point {\n const [x, y, , height] = this.boundingRect\n return [x, y + height * 0.5]\n }\n}\n","import type { NodeId } from '@/lib/litegraph/src/LGraphNode'\nimport type { LinkConnector } from '@/lib/litegraph/src/canvas/LinkConnector'\nimport { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'\nimport type {\n DefaultConnectionColors,\n Hoverable,\n INodeInputSlot,\n INodeOutputSlot,\n Point,\n Positionable\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport type {\n CanvasColour,\n CanvasPointer,\n CanvasPointerEvent,\n IContextMenuValue\n} from '@/lib/litegraph/src/litegraph'\nimport { snapPoint } from '@/lib/litegraph/src/measure'\nimport { CanvasItem } from '@/lib/litegraph/src/types/globalEnums'\nimport type {\n ExportedSubgraphIONode,\n Serialisable\n} from '@/lib/litegraph/src/types/serialisation'\n\nimport type { EmptySubgraphInput } from './EmptySubgraphInput'\nimport type { EmptySubgraphOutput } from './EmptySubgraphOutput'\nimport type { Subgraph } from './Subgraph'\nimport type { SubgraphInput } from './SubgraphInput'\nimport type { SubgraphOutput } from './SubgraphOutput'\n\nexport abstract class SubgraphIONodeBase<\n TSlot extends SubgraphInput | SubgraphOutput\n>\n implements Positionable, Hoverable, Serialisable<ExportedSubgraphIONode>\n{\n static margin = 10\n static minWidth = 100\n static roundedRadius = 10\n\n readonly #boundingRect: Rectangle = new Rectangle()\n\n abstract readonly id: NodeId\n\n get boundingRect(): Rectangle {\n return this.#boundingRect\n }\n\n selected: boolean = false\n pinned: boolean = false\n readonly removable = false\n\n isPointerOver: boolean = false\n\n abstract readonly emptySlot: EmptySubgraphInput | EmptySubgraphOutput\n\n get pos() {\n return this.boundingRect.pos\n }\n\n set pos(value) {\n this.boundingRect.pos = value\n }\n\n get size() {\n return this.boundingRect.size\n }\n\n set size(value) {\n this.boundingRect.size = value\n }\n\n protected get sideLineWidth(): number {\n return this.isPointerOver ? 2.5 : 2\n }\n\n protected get sideStrokeStyle(): CanvasColour {\n return this.isPointerOver ? 'white' : '#efefef'\n }\n\n abstract readonly slots: TSlot[]\n abstract get allSlots(): TSlot[]\n\n constructor(\n /** The subgraph that this node belongs to. */\n readonly subgraph: Subgraph\n ) {}\n\n move(deltaX: number, deltaY: number): void {\n this.pos[0] += deltaX\n this.pos[1] += deltaY\n }\n\n /** @inheritdoc */\n snapToGrid(snapTo: number): boolean {\n return this.pinned ? false : snapPoint(this.pos, snapTo)\n }\n\n abstract onPointerDown(\n e: CanvasPointerEvent,\n pointer: CanvasPointer,\n linkConnector: LinkConnector\n ): void\n\n // #region Hoverable\n\n containsPoint(point: Point): boolean {\n return this.boundingRect.containsPoint(point)\n }\n\n abstract get slotAnchorX(): number\n\n onPointerMove(e: CanvasPointerEvent): CanvasItem {\n const containsPoint = this.boundingRect.containsXy(e.canvasX, e.canvasY)\n let underPointer = containsPoint\n ? CanvasItem.SubgraphIoNode\n : CanvasItem.Nothing\n\n if (containsPoint) {\n if (!this.isPointerOver) this.onPointerEnter()\n\n for (const slot of this.allSlots) {\n slot.onPointerMove(e)\n if (slot.isPointerOver) underPointer |= CanvasItem.SubgraphIoSlot\n }\n } else if (this.isPointerOver) {\n this.onPointerLeave()\n }\n return underPointer\n }\n\n onPointerEnter() {\n this.isPointerOver = true\n }\n\n onPointerLeave() {\n this.isPointerOver = false\n\n for (const slot of this.slots) {\n slot.isPointerOver = false\n }\n }\n\n // #endregion Hoverable\n\n /**\n * Renames an IO slot in the subgraph.\n * @param slot The slot to rename.\n * @param name The new name for the slot.\n */\n abstract renameSlot(slot: TSlot, name: string): void\n\n /**\n * Removes an IO slot from the subgraph.\n * @param slot The slot to remove.\n */\n abstract removeSlot(slot: TSlot): void\n\n /**\n * Gets the slot at a given position in canvas space.\n * @param x The x coordinate of the position.\n * @param y The y coordinate of the position.\n * @returns The slot at the given position, otherwise `undefined`.\n */\n getSlotInPosition(x: number, y: number): TSlot | undefined {\n for (const slot of this.allSlots) {\n if (slot.boundingRect.containsXy(x, y)) {\n return slot\n }\n }\n }\n\n /**\n * Handles double-click on an IO slot to rename it.\n * @param slot The slot that was double-clicked.\n * @param event The event that triggered the double-click.\n */\n protected handleSlotDoubleClick(\n slot: TSlot,\n event: CanvasPointerEvent\n ): void {\n // Only allow renaming non-empty slots\n if (slot !== this.emptySlot) {\n this.#promptForSlotRename(slot, event)\n }\n }\n\n /**\n * Shows the context menu for an IO slot.\n * @param slot The slot to show the context menu for.\n * @param event The event that triggered the context menu.\n */\n protected showSlotContextMenu(slot: TSlot, event: CanvasPointerEvent): void {\n const options: (IContextMenuValue | null)[] = this.#getSlotMenuOptions(slot)\n if (!(options.length > 0)) return\n\n new LiteGraph.ContextMenu(options, {\n event,\n title: slot.name || 'Subgraph Output',\n callback: (item: IContextMenuValue) => {\n this.#onSlotMenuAction(item, slot, event)\n }\n })\n }\n\n /**\n * Gets the context menu options for an IO slot.\n * @param slot The slot to get the context menu options for.\n * @returns The context menu options.\n */\n #getSlotMenuOptions(slot: TSlot): (IContextMenuValue | null)[] {\n const options: (IContextMenuValue | null)[] = []\n\n // Disconnect option if slot has connections\n if (slot !== this.emptySlot && slot.linkIds.length > 0) {\n options.push({ content: 'Disconnect Links', value: 'disconnect' })\n }\n\n // Rename slot option (except for the empty slot)\n if (slot !== this.emptySlot) {\n options.push({ content: 'Rename Slot', value: 'rename' })\n }\n\n if (slot !== this.emptySlot) {\n options.push(null) // separator\n options.push({\n content: 'Remove Slot',\n value: 'remove',\n className: 'danger'\n })\n }\n\n return options\n }\n\n /**\n * Handles the action for an IO slot context menu.\n * @param selectedItem The item that was selected from the context menu.\n * @param slot The slot\n * @param event The event that triggered the context menu.\n */\n #onSlotMenuAction(\n selectedItem: IContextMenuValue,\n slot: TSlot,\n event: CanvasPointerEvent\n ): void {\n switch (selectedItem.value) {\n // Disconnect all links from this output\n case 'disconnect':\n slot.disconnect()\n break\n\n // Remove the slot\n case 'remove':\n if (slot !== this.emptySlot) {\n this.removeSlot(slot)\n }\n break\n\n // Rename the slot\n case 'rename':\n if (slot !== this.emptySlot) {\n this.#promptForSlotRename(slot, event)\n }\n break\n }\n\n this.subgraph.setDirtyCanvas(true)\n }\n\n /**\n * Prompts the user to rename a slot.\n * @param slot The slot to rename.\n * @param event The event that triggered the rename.\n */\n #promptForSlotRename(slot: TSlot, event: CanvasPointerEvent): void {\n this.subgraph.canvasAction((c) =>\n c.prompt(\n 'Slot name',\n slot.displayName,\n (newName: string) => {\n if (newName) this.renameSlot(slot, newName)\n },\n event\n )\n )\n }\n\n /** Arrange the slots in this node. */\n arrange(): void {\n const { minWidth, roundedRadius } = SubgraphIONodeBase\n const [, y] = this.boundingRect\n const x = this.slotAnchorX\n const { size } = this\n\n let maxWidth = minWidth\n let currentY = y + roundedRadius\n\n for (const slot of this.allSlots) {\n const [slotWidth, slotHeight] = slot.measure()\n slot.arrange([x, currentY, slotWidth, slotHeight])\n\n currentY += slotHeight\n if (slotWidth > maxWidth) maxWidth = slotWidth\n }\n\n size[0] = maxWidth + 2 * roundedRadius\n size[1] = currentY - y + roundedRadius\n }\n\n draw(\n ctx: CanvasRenderingContext2D,\n colorContext: DefaultConnectionColors,\n fromSlot?:\n | INodeInputSlot\n | INodeOutputSlot\n | SubgraphInput\n | SubgraphOutput,\n editorAlpha?: number\n ): void {\n const { lineWidth, strokeStyle, fillStyle, font, textBaseline } = ctx\n this.drawProtected(ctx, colorContext, fromSlot, editorAlpha)\n Object.assign(ctx, {\n lineWidth,\n strokeStyle,\n fillStyle,\n font,\n textBaseline\n })\n }\n\n /** @internal Leaves {@link ctx} dirty. */\n protected abstract drawProtected(\n ctx: CanvasRenderingContext2D,\n colorContext: DefaultConnectionColors,\n fromSlot?:\n | INodeInputSlot\n | INodeOutputSlot\n | SubgraphInput\n | SubgraphOutput,\n editorAlpha?: number\n ): void\n\n /** @internal Leaves {@link ctx} dirty. */\n protected drawSlots(\n ctx: CanvasRenderingContext2D,\n colorContext: DefaultConnectionColors,\n fromSlot?:\n | INodeInputSlot\n | INodeOutputSlot\n | SubgraphInput\n | SubgraphOutput,\n editorAlpha?: number\n ): void {\n ctx.fillStyle = '#AAA'\n ctx.font = '12px Inter, sans-serif'\n ctx.textBaseline = 'middle'\n\n for (const slot of this.allSlots) {\n slot.draw({ ctx, colorContext, fromSlot, editorAlpha })\n }\n }\n\n configure(data: ExportedSubgraphIONode): void {\n this.#boundingRect.set(data.bounding)\n this.pinned = data.pinned ?? false\n }\n\n asSerialisable(): ExportedSubgraphIONode {\n return {\n id: this.id,\n bounding: this.boundingRect.export(),\n pinned: this.pinned ? true : undefined\n }\n }\n}\n","import type { CanvasPointer } from '@/lib/litegraph/src/CanvasPointer'\nimport type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'\nimport { LLink } from '@/lib/litegraph/src/LLink'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport type { LinkConnector } from '@/lib/litegraph/src/canvas/LinkConnector'\nimport { SUBGRAPH_INPUT_ID } from '@/lib/litegraph/src/constants'\nimport type {\n DefaultConnectionColors,\n INodeInputSlot,\n INodeOutputSlot,\n ISlotType,\n Positionable\n} from '@/lib/litegraph/src/interfaces'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'\nimport { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'\nimport { findFreeSlotOfType } from '@/lib/litegraph/src/utils/collections'\n\nimport { EmptySubgraphInput } from './EmptySubgraphInput'\nimport { SubgraphIONodeBase } from './SubgraphIONodeBase'\nimport type { SubgraphInput } from './SubgraphInput'\nimport type { SubgraphOutput } from './SubgraphOutput'\n\nexport class SubgraphInputNode\n extends SubgraphIONodeBase<SubgraphInput>\n implements Positionable\n{\n readonly id: NodeId = SUBGRAPH_INPUT_ID\n\n readonly emptySlot: EmptySubgraphInput = new EmptySubgraphInput(this)\n\n get slots() {\n return this.subgraph.inputs\n }\n\n override get allSlots(): SubgraphInput[] {\n return [...this.slots, this.emptySlot]\n }\n\n get slotAnchorX() {\n const [x, , width] = this.boundingRect\n return x + width - SubgraphIONodeBase.roundedRadius\n }\n\n override onPointerDown(\n e: CanvasPointerEvent,\n pointer: CanvasPointer,\n linkConnector: LinkConnector\n ): void {\n // Left-click handling for dragging connections\n if (e.button === 0) {\n for (const slot of this.allSlots) {\n // Check if click is within the full slot area (including label)\n if (slot.boundingRect.containsXy(e.canvasX, e.canvasY)) {\n pointer.onDragStart = () => {\n linkConnector.dragNewFromSubgraphInput(this.subgraph, this, slot)\n }\n pointer.onDragEnd = (eUp) => {\n linkConnector.dropLinks(this.subgraph, eUp)\n }\n pointer.onDoubleClick = () => {\n this.handleSlotDoubleClick(slot, e)\n }\n pointer.finally = () => {\n linkConnector.reset(true)\n }\n }\n }\n // Check for right-click\n } else if (e.button === 2) {\n const slot = this.getSlotInPosition(e.canvasX, e.canvasY)\n if (slot) this.showSlotContextMenu(slot, e)\n }\n }\n\n /** @inheritdoc */\n override renameSlot(slot: SubgraphInput, name: string): void {\n this.subgraph.renameInput(slot, name)\n }\n\n /** @inheritdoc */\n override removeSlot(slot: SubgraphInput): void {\n this.subgraph.removeInput(slot)\n }\n\n canConnectTo(\n inputNode: NodeLike,\n input: INodeInputSlot,\n fromSlot: SubgraphInput\n ): boolean {\n return inputNode.canConnectTo(this, input, fromSlot)\n }\n\n connectSlots(\n fromSlot: SubgraphInput,\n inputNode: LGraphNode,\n input: INodeInputSlot,\n afterRerouteId: RerouteId | undefined\n ): LLink {\n const { subgraph } = this\n\n const outputIndex = this.slots.indexOf(fromSlot)\n const inputIndex = inputNode.inputs.indexOf(input)\n\n if (outputIndex === -1 || inputIndex === -1)\n throw new Error('Invalid slot indices.')\n\n return new LLink(\n ++subgraph.state.lastLinkId,\n input.type || fromSlot.type,\n this.id,\n outputIndex,\n inputNode.id,\n inputIndex,\n afterRerouteId\n )\n }\n\n // #region Legacy LGraphNode compatibility\n\n connectByType(\n slot: number,\n target_node: LGraphNode,\n target_slotType: ISlotType,\n optsIn?: { afterRerouteId?: RerouteId }\n ): LLink | undefined {\n const inputSlot = target_node.findInputByType(target_slotType)\n if (!inputSlot) return\n\n if (slot === -1) {\n // This indicates a connection is being made from the \"Empty\" slot.\n // We need to create a new, concrete input on the subgraph that matches the target.\n const newSubgraphInput = this.subgraph.addInput(\n inputSlot.slot.name,\n String(inputSlot.slot.type ?? '')\n )\n const newSlotIndex = this.slots.indexOf(newSubgraphInput)\n if (newSlotIndex === -1) {\n console.error('Could not find newly created subgraph input slot.')\n return\n }\n slot = newSlotIndex\n }\n\n return this.slots[slot].connect(\n inputSlot.slot,\n target_node,\n optsIn?.afterRerouteId\n )\n }\n\n findOutputSlot(name: string): SubgraphInput | undefined {\n return this.slots.find((output) => output.name === name)\n }\n\n findOutputByType(type: ISlotType): SubgraphInput | undefined {\n return findFreeSlotOfType(\n this.slots,\n type,\n (slot) => slot.linkIds.length > 0\n )?.slot\n }\n\n // #endregion Legacy LGraphNode compatibility\n\n _disconnectNodeInput(\n node: LGraphNode,\n input: INodeInputSlot,\n link: LLink | undefined\n ): void {\n const { subgraph } = this\n\n // Break floating links\n if (input._floatingLinks?.size) {\n for (const link of input._floatingLinks) {\n subgraph.removeFloatingLink(link)\n }\n }\n\n input.link = null\n subgraph.setDirtyCanvas(false, true)\n\n if (!link) return\n\n const subgraphInputIndex = link.origin_slot\n link.disconnect(subgraph, 'output')\n subgraph._version++\n\n const subgraphInput = this.slots.at(subgraphInputIndex)\n if (!subgraphInput) {\n console.warn(\n 'disconnectNodeInput: subgraphInput not found',\n this,\n subgraphInputIndex\n )\n return\n }\n\n // search in the inputs list for this link\n const index = subgraphInput.linkIds.indexOf(link.id)\n if (index !== -1) {\n subgraphInput.linkIds.splice(index, 1)\n } else {\n console.warn(\n 'disconnectNodeInput: link ID not found in subgraphInput linkIds',\n link.id\n )\n }\n const slotIndex = node.inputs.findIndex((inp) => inp === input)\n if (slotIndex !== -1) {\n node.onConnectionsChange?.(\n NodeSlotType.INPUT,\n slotIndex,\n false,\n link,\n subgraphInput\n )\n }\n }\n\n override drawProtected(\n ctx: CanvasRenderingContext2D,\n colorContext: DefaultConnectionColors,\n fromSlot?:\n | INodeInputSlot\n | INodeOutputSlot\n | SubgraphInput\n | SubgraphOutput,\n editorAlpha?: number\n ): void {\n const { roundedRadius } = SubgraphIONodeBase\n const transform = ctx.getTransform()\n\n const [x, y, width, height] = this.boundingRect\n ctx.translate(x, y)\n\n // Draw top rounded part\n ctx.strokeStyle = this.sideStrokeStyle\n ctx.lineWidth = this.sideLineWidth\n ctx.beginPath()\n ctx.arc(\n width - roundedRadius,\n roundedRadius,\n roundedRadius,\n Math.PI * 1.5,\n 0\n )\n\n // Straight line to bottom\n ctx.moveTo(width, roundedRadius)\n ctx.lineTo(width, height - roundedRadius)\n\n // Bottom rounded part\n ctx.arc(\n width - roundedRadius,\n height - roundedRadius,\n roundedRadius,\n 0,\n Math.PI * 0.5\n )\n ctx.stroke()\n\n // Restore context\n ctx.setTransform(transform)\n\n this.drawSlots(ctx, colorContext, fromSlot, editorAlpha)\n }\n}\n","import { pull } from 'es-toolkit/compat'\n\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { LLink } from '@/lib/litegraph/src/LLink'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n Point,\n ReadOnlyRect\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'\n\nimport type { SubgraphInput } from './SubgraphInput'\nimport type { SubgraphOutputNode } from './SubgraphOutputNode'\nimport { SubgraphSlot } from './SubgraphSlotBase'\nimport { isNodeSlot, isSubgraphInput } from './subgraphUtils'\n\n/**\n * An output \"slot\" from a subgraph to a parent graph.\n *\n * IMPORTANT: A subgraph \"output\" is both an output AND an input. It creates an extra link connection point between\n * a parent graph and a subgraph, so is conceptually similar to a reroute.\n *\n * This can be a little confusing, but is easier to visualise when imagining editing a subgraph.\n * You have \"Subgraph Outputs\", because they go from inside the subgraph and out, but links to them come from \"node outputs\".\n *\n * Functionally, however, when editing a subgraph, that \"subgraph output\" is the \"target\" or \"input side\" of a link.\n */\nexport class SubgraphOutput extends SubgraphSlot {\n declare parent: SubgraphOutputNode\n\n override connect(\n slot: INodeOutputSlot,\n node: LGraphNode,\n afterRerouteId?: RerouteId\n ): LLink | undefined {\n const { subgraph } = this.parent\n\n // Validate type compatibility\n if (!LiteGraph.isValidConnection(slot.type, this.type)) return\n\n // Allow nodes to block connection\n const outputIndex = node.outputs.indexOf(slot)\n if (outputIndex === -1)\n throw new Error('Slot is not an output of the given node')\n\n if (\n node.onConnectOutput?.(outputIndex, this.type, this, this.parent, -1) ===\n false\n )\n return\n\n // Link should not be present, but just in case, disconnect it\n const existingLink = this.getLinks().at(0)\n if (existingLink != null) {\n subgraph.beforeChange()\n\n existingLink.disconnect(subgraph, 'input')\n const resolved = existingLink.resolve(subgraph)\n const links = resolved.output?.links\n if (links) pull(links, existingLink.id)\n }\n\n const link = new LLink(\n ++subgraph.state.lastLinkId,\n slot.type,\n node.id,\n outputIndex,\n this.parent.id,\n this.parent.slots.indexOf(this),\n afterRerouteId\n )\n\n // Add to graph links list\n subgraph._links.set(link.id, link)\n\n // Set link ID in each slot\n this.linkIds[0] = link.id\n slot.links ??= []\n slot.links.push(link.id)\n\n // Reroutes\n const reroutes = LLink.getReroutes(subgraph, link)\n for (const reroute of reroutes) {\n reroute.linkIds.add(link.id)\n if (reroute.floating) delete reroute.floating\n reroute._dragging = undefined\n }\n\n // If this is the terminus of a floating link, remove it\n const lastReroute = reroutes.at(-1)\n if (lastReroute) {\n for (const linkId of lastReroute.floatingLinkIds) {\n const link = subgraph.floatingLinks.get(linkId)\n if (link?.parentId === lastReroute.id) {\n subgraph.removeFloatingLink(link)\n }\n }\n }\n subgraph._version++\n\n node.onConnectionsChange?.(\n NodeSlotType.OUTPUT,\n outputIndex,\n true,\n link,\n slot\n )\n\n subgraph.afterChange()\n\n return link\n }\n\n get labelPos(): Point {\n const [x, y, , height] = this.boundingRect\n return [x + height, y + height * 0.5]\n }\n\n override arrange(rect: ReadOnlyRect): void {\n const [left, top, width, height] = rect\n const { boundingRect: b, pos } = this\n\n b[0] = left\n b[1] = top\n b[2] = width\n b[3] = height\n\n pos[0] = left + height * 0.5\n pos[1] = top + height * 0.5\n }\n\n /**\n * Checks if this slot is a valid target for a connection from the given slot.\n * For SubgraphOutput (which acts as an input inside the subgraph),\n * the fromSlot should be an output slot.\n */\n override isValidTarget(\n fromSlot: INodeInputSlot | INodeOutputSlot | SubgraphInput | SubgraphOutput\n ): boolean {\n if (isNodeSlot(fromSlot)) {\n return (\n 'links' in fromSlot &&\n LiteGraph.isValidConnection(fromSlot.type, this.type)\n )\n }\n\n if (isSubgraphInput(fromSlot)) {\n return LiteGraph.isValidConnection(fromSlot.type, this.type)\n }\n\n return false\n }\n override disconnect() {\n const { subgraph } = this.parent\n //should never have more than one connection\n for (const linkId of this.linkIds) {\n const link = subgraph.links[linkId]\n if (!link) continue\n subgraph.removeLink(linkId)\n const { output, outputNode } = link.resolve(subgraph)\n if (output)\n output.links = output.links?.filter((id) => id !== linkId) ?? null\n outputNode?.onConnectionsChange?.(\n NodeSlotType.OUTPUT,\n link.origin_slot,\n false,\n link,\n this\n )\n }\n this.linkIds.length = 0\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport type { INodeOutputSlot, Point } from '@/lib/litegraph/src/interfaces'\nimport { nextUniqueName } from '@/lib/litegraph/src/strings'\nimport { zeroUuid } from '@/lib/litegraph/src/utils/uuid'\n\nimport { SubgraphOutput } from './SubgraphOutput'\nimport type { SubgraphOutputNode } from './SubgraphOutputNode'\n\n/**\n * A virtual slot that simply creates a new output slot when connected to.\n */\nexport class EmptySubgraphOutput extends SubgraphOutput {\n declare parent: SubgraphOutputNode\n\n constructor(parent: SubgraphOutputNode) {\n super(\n {\n id: zeroUuid,\n name: '',\n type: ''\n },\n parent\n )\n }\n\n override connect(\n slot: INodeOutputSlot,\n node: LGraphNode,\n afterRerouteId?: RerouteId\n ): LLink | undefined {\n const { subgraph } = this.parent\n const existingNames = subgraph.outputs.map((x) => x.name)\n\n const name = nextUniqueName(slot.name, existingNames)\n const output = subgraph.addOutput(name, String(slot.type))\n return output.connect(slot, node, afterRerouteId)\n }\n\n override get labelPos(): Point {\n const [x, y, , height] = this.boundingRect\n return [x, y + height * 0.5]\n }\n}\n","import type { CanvasPointer } from '@/lib/litegraph/src/CanvasPointer'\nimport type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport type { LinkConnector } from '@/lib/litegraph/src/canvas/LinkConnector'\nimport { SUBGRAPH_OUTPUT_ID } from '@/lib/litegraph/src/constants'\nimport type {\n DefaultConnectionColors,\n INodeInputSlot,\n INodeOutputSlot,\n ISlotType,\n Positionable\n} from '@/lib/litegraph/src/interfaces'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'\nimport type { SubgraphIO } from '@/lib/litegraph/src/types/serialisation'\nimport { findFreeSlotOfType } from '@/lib/litegraph/src/utils/collections'\n\nimport { EmptySubgraphOutput } from './EmptySubgraphOutput'\nimport { SubgraphIONodeBase } from './SubgraphIONodeBase'\nimport type { SubgraphInput } from './SubgraphInput'\nimport type { SubgraphOutput } from './SubgraphOutput'\n\nexport class SubgraphOutputNode\n extends SubgraphIONodeBase<SubgraphOutput>\n implements Positionable\n{\n readonly id: NodeId = SUBGRAPH_OUTPUT_ID\n\n readonly emptySlot: EmptySubgraphOutput = new EmptySubgraphOutput(this)\n\n get slots() {\n return this.subgraph.outputs\n }\n\n override get allSlots(): SubgraphOutput[] {\n return [...this.slots, this.emptySlot]\n }\n\n get slotAnchorX() {\n const [x] = this.boundingRect\n return x + SubgraphIONodeBase.roundedRadius\n }\n\n override onPointerDown(\n e: CanvasPointerEvent,\n pointer: CanvasPointer,\n linkConnector: LinkConnector\n ): void {\n // Left-click handling for dragging connections\n if (e.button === 0) {\n for (const slot of this.allSlots) {\n // Check if click is within the full slot area (including label)\n if (slot.boundingRect.containsXy(e.canvasX, e.canvasY)) {\n pointer.onDragStart = () => {\n linkConnector.dragNewFromSubgraphOutput(this.subgraph, this, slot)\n }\n pointer.onDragEnd = (eUp) => {\n linkConnector.dropLinks(this.subgraph, eUp)\n }\n pointer.onDoubleClick = () => {\n this.handleSlotDoubleClick(slot, e)\n }\n pointer.finally = () => {\n linkConnector.reset(true)\n }\n }\n }\n // Check for right-click\n } else if (e.button === 2) {\n const slot = this.getSlotInPosition(e.canvasX, e.canvasY)\n if (slot) this.showSlotContextMenu(slot, e)\n }\n }\n\n /** @inheritdoc */\n override renameSlot(slot: SubgraphOutput, name: string): void {\n this.subgraph.renameOutput(slot, name)\n }\n\n /** @inheritdoc */\n override removeSlot(slot: SubgraphOutput): void {\n this.subgraph.removeOutput(slot)\n }\n\n canConnectTo(\n outputNode: NodeLike,\n fromSlot: SubgraphOutput,\n output: INodeOutputSlot | SubgraphIO\n ): boolean {\n return outputNode.canConnectTo(this, fromSlot, output)\n }\n\n connectByTypeOutput(\n slot: number,\n target_node: LGraphNode,\n target_slotType: ISlotType,\n optsIn?: { afterRerouteId?: RerouteId }\n ): LLink | undefined {\n const outputSlot = target_node.findOutputByType(target_slotType)\n if (!outputSlot) return\n\n return this.slots[slot].connect(\n outputSlot.slot,\n target_node,\n optsIn?.afterRerouteId\n )\n }\n\n findInputByType(type: ISlotType): SubgraphOutput | undefined {\n return findFreeSlotOfType(\n this.slots,\n type,\n (slot) => slot.linkIds.length > 0\n )?.slot\n }\n\n override drawProtected(\n ctx: CanvasRenderingContext2D,\n colorContext: DefaultConnectionColors,\n fromSlot?:\n | INodeInputSlot\n | INodeOutputSlot\n | SubgraphInput\n | SubgraphOutput,\n editorAlpha?: number\n ): void {\n const { roundedRadius } = SubgraphIONodeBase\n const transform = ctx.getTransform()\n\n const [x, y, , height] = this.boundingRect\n ctx.translate(x, y)\n\n // Draw bottom rounded part\n ctx.strokeStyle = this.sideStrokeStyle\n ctx.lineWidth = this.sideLineWidth\n ctx.beginPath()\n ctx.arc(roundedRadius, roundedRadius, roundedRadius, Math.PI, Math.PI * 1.5)\n\n // Straight line to bottom\n ctx.moveTo(0, roundedRadius)\n ctx.lineTo(0, height - roundedRadius)\n\n // Bottom rounded part\n ctx.arc(\n roundedRadius,\n height - roundedRadius,\n roundedRadius,\n Math.PI,\n Math.PI * 0.5,\n true\n )\n ctx.stroke()\n\n // Restore context\n ctx.setTransform(transform)\n\n this.drawSlots(ctx, colorContext, fromSlot, editorAlpha)\n }\n}\n","import type { LGraph } from '@/lib/litegraph/src/LGraph'\nimport { LGraphGroup } from '@/lib/litegraph/src/LGraphGroup'\nimport { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { LLink } from '@/lib/litegraph/src/LLink'\nimport type { ResolvedConnection } from '@/lib/litegraph/src/LLink'\nimport { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { RerouteId } from '@/lib/litegraph/src/Reroute'\nimport {\n SUBGRAPH_INPUT_ID,\n SUBGRAPH_OUTPUT_ID\n} from '@/lib/litegraph/src/constants'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n Positionable\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph, createUuidv4 } from '@/lib/litegraph/src/litegraph'\nimport { nextUniqueName } from '@/lib/litegraph/src/strings'\nimport type {\n ISerialisedNode,\n SerialisableLLink,\n SubgraphIO\n} from '@/lib/litegraph/src/types/serialisation'\nimport type { UUID } from '@/lib/litegraph/src/utils/uuid'\n\nimport type { GraphOrSubgraph } from './Subgraph'\nimport type { SubgraphInput } from './SubgraphInput'\nimport { SubgraphInputNode } from './SubgraphInputNode'\nimport type { SubgraphOutput } from './SubgraphOutput'\nimport { SubgraphOutputNode } from './SubgraphOutputNode'\n\ninterface FilteredItems {\n nodes: Set<LGraphNode>\n reroutes: Set<Reroute>\n groups: Set<LGraphGroup>\n subgraphInputNodes: Set<SubgraphInputNode>\n subgraphOutputNodes: Set<SubgraphOutputNode>\n unknown: Set<Positionable>\n}\n\nexport function splitPositionables(\n items: Iterable<Positionable>\n): FilteredItems {\n const nodes = new Set<LGraphNode>()\n const reroutes = new Set<Reroute>()\n const groups = new Set<LGraphGroup>()\n const subgraphInputNodes = new Set<SubgraphInputNode>()\n const subgraphOutputNodes = new Set<SubgraphOutputNode>()\n\n const unknown = new Set<Positionable>()\n\n for (const item of items) {\n switch (true) {\n case item instanceof LGraphNode:\n nodes.add(item)\n break\n case item instanceof LGraphGroup:\n groups.add(item)\n break\n case item instanceof Reroute:\n reroutes.add(item)\n break\n case item instanceof SubgraphInputNode:\n subgraphInputNodes.add(item)\n break\n case item instanceof SubgraphOutputNode:\n subgraphOutputNodes.add(item)\n break\n default:\n unknown.add(item)\n break\n }\n }\n\n return {\n nodes,\n reroutes,\n groups,\n subgraphInputNodes,\n subgraphOutputNodes,\n unknown\n }\n}\n\ninterface BoundaryLinks {\n boundaryLinks: LLink[]\n boundaryFloatingLinks: LLink[]\n internalLinks: LLink[]\n boundaryInputLinks: LLink[]\n boundaryOutputLinks: LLink[]\n}\n\nexport function getBoundaryLinks(\n graph: LGraph,\n items: Set<Positionable>\n): BoundaryLinks {\n const internalLinks: LLink[] = []\n const boundaryLinks: LLink[] = []\n const boundaryInputLinks: LLink[] = []\n const boundaryOutputLinks: LLink[] = []\n const boundaryFloatingLinks: LLink[] = []\n const visited = new WeakSet<Positionable>()\n\n for (const item of items) {\n if (visited.has(item)) continue\n visited.add(item)\n\n // Nodes\n if (item instanceof LGraphNode) {\n const node = item\n\n // Inputs\n if (node.inputs) {\n for (const input of node.inputs) {\n addFloatingLinks(input._floatingLinks)\n\n if (input.link == null) continue\n\n const resolved = LLink.resolve(input.link, graph)\n if (!resolved) {\n console.warn(`Failed to resolve link ID [${input.link}]`)\n continue\n }\n\n // Output end of this link is outside the items set\n const { link, outputNode } = resolved\n if (outputNode) {\n if (!items.has(outputNode)) {\n boundaryInputLinks.push(link)\n } else {\n internalLinks.push(link)\n }\n } else if (link.origin_id === SUBGRAPH_INPUT_ID) {\n // Subgraph input node - always boundary\n boundaryInputLinks.push(link)\n }\n }\n }\n\n // Outputs\n if (node.outputs) {\n for (const output of node.outputs) {\n addFloatingLinks(output._floatingLinks)\n\n if (!output.links) continue\n\n const many = LLink.resolveMany(output.links, graph)\n for (const { link, inputNode } of many) {\n if (\n // Subgraph output node\n link.target_id === SUBGRAPH_OUTPUT_ID ||\n // Input end of this link is outside the items set\n (inputNode && !items.has(inputNode))\n ) {\n boundaryOutputLinks.push(link)\n }\n // Internal links are discovered on input side.\n }\n }\n }\n } else if (item instanceof Reroute) {\n // Reroutes\n const reroute = item\n\n // TODO: This reroute should be on one side of the boundary. We should mark the reroute that is on each side of the boundary.\n // TODO: This could occur any number of times on a link; each time should be marked as a separate boundary.\n // TODO: e.g. A link with 3 reroutes, the first and last reroute are in `items`, but the middle reroute is not. This will be two \"in\" and two \"out\" boundaries.\n const results = LLink.resolveMany(reroute.linkIds, graph)\n for (const { link } of results) {\n const reroutes = LLink.getReroutes(graph, link)\n const reroutesOutside = reroutes.filter(\n (reroute) => !items.has(reroute)\n )\n\n // for (const reroute of reroutes) {\n // // TODO: Do the checks here.\n // }\n\n const { inputNode, outputNode } = link.resolve(graph)\n\n if (\n reroutesOutside.length ||\n (inputNode && !items.has(inputNode)) ||\n (outputNode && !items.has(outputNode))\n ) {\n boundaryLinks.push(link)\n }\n }\n }\n }\n\n return {\n boundaryLinks,\n boundaryFloatingLinks,\n internalLinks,\n boundaryInputLinks,\n boundaryOutputLinks\n }\n\n /**\n * Adds any floating links that cross the boundary.\n * @param floatingLinks The floating links to check\n */\n function addFloatingLinks(floatingLinks: Set<LLink> | undefined): void {\n if (!floatingLinks) return\n\n for (const link of floatingLinks) {\n const crossesBoundary = LLink.getReroutes(graph, link).some(\n (reroute) => !items.has(reroute)\n )\n\n if (crossesBoundary) boundaryFloatingLinks.push(link)\n }\n }\n}\n\nexport function multiClone(nodes: Iterable<LGraphNode>): ISerialisedNode[] {\n const clonedNodes: ISerialisedNode[] = []\n\n // Selectively clone - keep IDs & links\n for (const node of nodes) {\n const newNode = LiteGraph.createNode(node.type)\n if (!newNode) {\n console.warn('Failed to create node', node.type)\n const serializedData = structuredClone(node.serialize())\n clonedNodes.push(serializedData)\n continue\n }\n\n // Must be cloned; litegraph \"serialize\" is mostly shallow clone\n const data = structuredClone(node.serialize())\n newNode.configure(data)\n\n clonedNodes.push(newNode.serialize())\n }\n\n return clonedNodes\n}\n\n/**\n * Groups resolved connections by output object. If the output is nullish, the connection will be in its own group.\n * @param resolvedConnections The resolved connections to group\n * @returns A map of grouped connections.\n */\nexport function groupResolvedByOutput(\n resolvedConnections: ResolvedConnection[]\n): Map<SubgraphIO | INodeOutputSlot | object, ResolvedConnection[]> {\n const groupedByOutput: ReturnType<typeof groupResolvedByOutput> = new Map()\n\n for (const resolved of resolvedConnections) {\n // Force no group (unique object) if output is undefined; corruption or an error has occurred\n const groupBy = resolved.subgraphInput ?? resolved.output ?? {}\n const group = groupedByOutput.get(groupBy)\n if (group) {\n group.push(resolved)\n } else {\n groupedByOutput.set(groupBy, [resolved])\n }\n }\n\n return groupedByOutput\n}\nfunction mapReroutes(\n link: SerialisableLLink,\n reroutes: Map<RerouteId, Reroute>\n) {\n let child: SerialisableLLink | Reroute = link\n let nextReroute =\n child.parentId === undefined ? undefined : reroutes.get(child.parentId)\n\n while (child.parentId !== undefined && nextReroute) {\n child = nextReroute\n nextReroute =\n child.parentId === undefined ? undefined : reroutes.get(child.parentId)\n }\n\n const lastId = child.parentId\n child.parentId = undefined\n return lastId\n}\n\nexport function mapSubgraphInputsAndLinks(\n resolvedInputLinks: ResolvedConnection[],\n links: SerialisableLLink[],\n reroutes: Map<RerouteId, Reroute>\n): SubgraphIO[] {\n // Group matching links\n const groupedByOutput = groupResolvedByOutput(resolvedInputLinks)\n\n // Create one input for each output (outside subgraph)\n const inputs: SubgraphIO[] = []\n\n for (const [, connections] of groupedByOutput) {\n const inputLinks: SerialisableLLink[] = []\n\n // Create serialised links for all links (will be recreated in subgraph)\n for (const resolved of connections) {\n const { link, input } = resolved\n if (!input) continue\n\n const linkData = link.asSerialisable()\n link.parentId = mapReroutes(link, reroutes)\n linkData.origin_id = SUBGRAPH_INPUT_ID\n linkData.origin_slot = inputs.length\n\n links.push(linkData)\n inputLinks.push(linkData)\n }\n\n // Use first input link\n const { input } = connections[0]\n if (!input) continue\n\n // Subgraph input slot\n const {\n color_off,\n color_on,\n dir,\n hasErrors,\n label,\n localized_name,\n name,\n shape,\n type\n } = input\n const uniqueName = nextUniqueName(\n name,\n inputs.map((input) => input.name)\n )\n const uniqueLocalizedName = localized_name\n ? nextUniqueName(\n localized_name,\n inputs.map((input) => input.localized_name ?? '')\n )\n : undefined\n\n const inputData: SubgraphIO = {\n id: createUuidv4(),\n type: String(type),\n linkIds: inputLinks.map((link) => link.id),\n name: uniqueName,\n color_off,\n color_on,\n dir,\n label,\n localized_name: uniqueLocalizedName,\n hasErrors,\n shape\n }\n\n inputs.push(inputData)\n }\n\n return inputs\n}\n\n/**\n * Clones the output slots, and updates existing links, when converting items to a subgraph.\n * @param resolvedOutputLinks The resolved output links.\n * @param links The links to add to the subgraph.\n * @returns The subgraph output slots.\n */\nexport function mapSubgraphOutputsAndLinks(\n resolvedOutputLinks: ResolvedConnection[],\n links: SerialisableLLink[],\n reroutes: Map<RerouteId, Reroute>\n): SubgraphIO[] {\n // Group matching links\n const groupedByOutput = groupResolvedByOutput(resolvedOutputLinks)\n\n const outputs: SubgraphIO[] = []\n\n for (const [, connections] of groupedByOutput) {\n const outputLinks: SerialisableLLink[] = []\n\n // Create serialised links for all links (will be recreated in subgraph)\n for (const resolved of connections) {\n const { link, output } = resolved\n if (!output) continue\n\n const linkData = link.asSerialisable()\n linkData.parentId = mapReroutes(link, reroutes)\n linkData.target_id = SUBGRAPH_OUTPUT_ID\n linkData.target_slot = outputs.length\n\n links.push(linkData)\n outputLinks.push(linkData)\n }\n\n // Use first output link\n const { output } = connections[0]\n if (!output) continue\n\n // Subgraph output slot\n const {\n color_off,\n color_on,\n dir,\n hasErrors,\n label,\n localized_name,\n name,\n shape,\n type\n } = output\n const uniqueName = nextUniqueName(\n name,\n outputs.map((output) => output.name)\n )\n const uniqueLocalizedName = localized_name\n ? nextUniqueName(\n localized_name,\n outputs.map((output) => output.localized_name ?? '')\n )\n : undefined\n\n const outputData = {\n id: createUuidv4(),\n type: String(type),\n linkIds: outputLinks.map((link) => link.id),\n name: uniqueName,\n color_off,\n color_on,\n dir,\n label,\n localized_name: uniqueLocalizedName,\n hasErrors,\n shape\n } satisfies SubgraphIO\n\n outputs.push(structuredClone(outputData))\n }\n return outputs\n}\n\n/**\n * Collects all subgraph IDs used directly in a single graph (non-recursive).\n * @param graph The graph to check for subgraph nodes\n * @returns Set of subgraph IDs used in this graph\n */\nexport function getDirectSubgraphIds(graph: GraphOrSubgraph): Set<UUID> {\n const subgraphIds = new Set<UUID>()\n\n for (const node of graph._nodes) {\n if (node.isSubgraphNode()) {\n subgraphIds.add(node.type)\n }\n }\n\n return subgraphIds\n}\n\n/**\n * Collects all subgraph IDs referenced in a graph hierarchy using BFS.\n * @param rootGraph The graph to start from\n * @param subgraphRegistry Map of all available subgraphs\n * @returns Set of all subgraph IDs found\n */\nexport function findUsedSubgraphIds(\n rootGraph: GraphOrSubgraph,\n subgraphRegistry: Map<UUID, GraphOrSubgraph>\n): Set<UUID> {\n const usedSubgraphIds = new Set<UUID>()\n const toVisit: GraphOrSubgraph[] = [rootGraph]\n\n while (toVisit.length > 0) {\n const graph = toVisit.shift()!\n const directIds = getDirectSubgraphIds(graph)\n\n for (const id of directIds) {\n if (!usedSubgraphIds.has(id)) {\n usedSubgraphIds.add(id)\n const subgraph = subgraphRegistry.get(id)\n if (subgraph) {\n toVisit.push(subgraph)\n }\n }\n }\n }\n\n return usedSubgraphIds\n}\n\n/**\n * Type guard to check if a slot is a SubgraphInput.\n * @param slot The slot to check\n * @returns true if the slot is a SubgraphInput\n */\nexport function isSubgraphInput(slot: unknown): slot is SubgraphInput {\n return (\n slot != null &&\n typeof slot === 'object' &&\n 'parent' in slot &&\n slot.parent instanceof SubgraphInputNode\n )\n}\n\n/**\n * Type guard to check if a slot is a SubgraphOutput.\n * @param slot The slot to check\n * @returns true if the slot is a SubgraphOutput\n */\nexport function isSubgraphOutput(slot: unknown): slot is SubgraphOutput {\n return (\n slot != null &&\n typeof slot === 'object' &&\n 'parent' in slot &&\n slot.parent instanceof SubgraphOutputNode\n )\n}\n\n/**\n * Type guard to check if a slot is a regular node slot (INodeInputSlot or INodeOutputSlot).\n * @param slot The slot to check\n * @returns true if the slot is a regular node slot\n */\nexport function isNodeSlot(\n slot: unknown\n): slot is INodeInputSlot | INodeOutputSlot {\n return (\n slot != null &&\n typeof slot === 'object' &&\n ('link' in slot || 'links' in slot)\n )\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LinkId } from '@/lib/litegraph/src/LLink'\nimport { LabelPosition } from '@/lib/litegraph/src/draw'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n OptionalProps,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport { NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'\nimport type { IDrawOptions } from '@/lib/litegraph/src/node/NodeSlot'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport { isSubgraphInput } from '@/lib/litegraph/src/subgraph/subgraphUtils'\nimport type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'\n\nexport class NodeInputSlot extends NodeSlot implements INodeInputSlot {\n link: LinkId | null\n alwaysVisible?: boolean\n\n get isWidgetInputSlot(): boolean {\n return !!this.widget\n }\n\n #widget: WeakRef<IBaseWidget> | undefined\n\n /** Internal use only; API is not finalised and may change at any time. */\n get _widget(): IBaseWidget | undefined {\n return this.#widget?.deref()\n }\n\n set _widget(widget: IBaseWidget | undefined) {\n this.#widget = widget ? new WeakRef(widget) : undefined\n }\n\n get collapsedPos(): Readonly<Point> {\n return [0, LiteGraph.NODE_TITLE_HEIGHT * -0.5]\n }\n\n constructor(\n slot: OptionalProps<INodeInputSlot, 'boundingRect'>,\n node: LGraphNode\n ) {\n super(slot, node)\n this.link = slot.link\n }\n\n override get isConnected(): boolean {\n return this.link != null\n }\n\n override isValidTarget(\n fromSlot: INodeInputSlot | INodeOutputSlot | SubgraphInput | SubgraphOutput\n ): boolean {\n if ('links' in fromSlot) {\n return LiteGraph.isValidConnection(fromSlot.type, this.type)\n }\n\n if (isSubgraphInput(fromSlot)) {\n return LiteGraph.isValidConnection(fromSlot.type, this.type)\n }\n\n return false\n }\n\n override draw(\n ctx: CanvasRenderingContext2D,\n options: Omit<IDrawOptions, 'doStroke' | 'labelPosition'>\n ) {\n const { textAlign } = ctx\n ctx.textAlign = 'left'\n\n super.draw(ctx, {\n ...options,\n labelPosition: LabelPosition.Right,\n doStroke: false\n })\n\n ctx.textAlign = textAlign\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LinkId } from '@/lib/litegraph/src/LLink'\nimport { LabelPosition } from '@/lib/litegraph/src/draw'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n OptionalProps,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport { NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'\nimport type { IDrawOptions } from '@/lib/litegraph/src/node/NodeSlot'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport { isSubgraphOutput } from '@/lib/litegraph/src/subgraph/subgraphUtils'\n\nexport class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {\n #node: LGraphNode\n\n links: LinkId[] | null\n _data?: unknown\n slot_index?: number\n\n get isWidgetInputSlot(): false {\n return false\n }\n\n get collapsedPos(): Readonly<Point> {\n return [\n this.#node._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH,\n LiteGraph.NODE_TITLE_HEIGHT * -0.5\n ]\n }\n\n constructor(\n slot: OptionalProps<INodeOutputSlot, 'boundingRect'>,\n node: LGraphNode\n ) {\n super(slot, node)\n this.links = slot.links\n this._data = slot._data\n this.slot_index = slot.slot_index\n this.#node = node\n }\n\n override isValidTarget(\n fromSlot: INodeInputSlot | INodeOutputSlot | SubgraphInput | SubgraphOutput\n ): boolean {\n if ('link' in fromSlot) {\n return LiteGraph.isValidConnection(this.type, fromSlot.type)\n }\n\n if (isSubgraphOutput(fromSlot)) {\n return LiteGraph.isValidConnection(this.type, fromSlot.type)\n }\n\n return false\n }\n\n override get isConnected(): boolean {\n return this.links != null && this.links.length > 0\n }\n\n override draw(\n ctx: CanvasRenderingContext2D,\n options: Omit<IDrawOptions, 'doStroke' | 'labelPosition'>\n ) {\n const { textAlign, strokeStyle } = ctx\n ctx.textAlign = 'right'\n ctx.strokeStyle = 'black'\n\n super.draw(ctx, {\n ...options,\n labelPosition: LabelPosition.Left,\n doStroke: true\n })\n\n ctx.textAlign = textAlign\n ctx.strokeStyle = strokeStyle\n }\n}\n","import { LiteGraph } from '@/lib/litegraph/src/litegraph'\n\n/** Guard against unbound allocation. */\nconst UNIQUE_MESSAGE_LIMIT = 10_000\nconst sentWarnings: Set<string> = new Set()\n\n/**\n * Warns that a deprecated function has been used via the public\n * {@link onDeprecationWarning} / {@link onEveryDeprecationWarning} callback arrays.\n * @param message Plain-language detail about what has been deprecated. This **should not** include unique data; use {@link source}.\n * @param source A reference object to include alongside the message, e.g. `this`.\n */\nexport function warnDeprecated(message: string, source?: object): void {\n if (!LiteGraph.alwaysRepeatWarnings) {\n // Do not repeat\n if (sentWarnings.has(message)) return\n\n // Hard limit of unique messages per session\n if (sentWarnings.size > UNIQUE_MESSAGE_LIMIT) return\n\n sentWarnings.add(message)\n }\n\n for (const callback of LiteGraph.onDeprecationWarning) {\n callback(message, source)\n }\n}\n","export interface SpaceRequest {\n minSize: number\n maxSize?: number\n}\n\n/**\n * Distributes available space among items with min/max size constraints\n * @param totalSpace Total space available to distribute\n * @param requests Array of space requests with size constraints\n * @returns Array of space allocations\n */\nexport function distributeSpace(\n totalSpace: number,\n requests: SpaceRequest[]\n): number[] {\n // Handle edge cases\n if (requests.length === 0) return []\n\n // Calculate total minimum space needed\n const totalMinSize = requests.reduce((sum, req) => sum + req.minSize, 0)\n\n // If we can't meet minimum requirements, return the minimum sizes\n if (totalSpace < totalMinSize) {\n return requests.map((req) => req.minSize)\n }\n\n // Initialize allocations with minimum sizes\n let allocations = requests.map((req) => ({\n computedSize: req.minSize,\n maxSize: req.maxSize ?? Infinity,\n remaining: (req.maxSize ?? Infinity) - req.minSize\n }))\n\n // Calculate remaining space to distribute\n let remainingSpace = totalSpace - totalMinSize\n\n // Distribute remaining space iteratively\n while (\n remainingSpace > 0 &&\n allocations.some((alloc) => alloc.remaining > 0)\n ) {\n // Count items that can still grow\n const growableItems = allocations.filter(\n (alloc) => alloc.remaining > 0\n ).length\n\n if (growableItems === 0) break\n\n // Calculate fair share per item\n const sharePerItem = remainingSpace / growableItems\n\n // Track how much space was actually used in this iteration\n let spaceUsedThisRound = 0\n\n // Distribute space\n allocations = allocations.map((alloc) => {\n if (alloc.remaining <= 0) return alloc\n\n const growth = Math.min(sharePerItem, alloc.remaining)\n spaceUsedThisRound += growth\n\n return {\n ...alloc,\n computedSize: alloc.computedSize + growth,\n remaining: alloc.remaining - growth\n }\n })\n\n remainingSpace -= spaceUsedThisRound\n\n // Break if we couldn't distribute any more space\n if (spaceUsedThisRound === 0) break\n }\n\n // Return only the computed sizes\n return allocations.map(({ computedSize }) => computedSize)\n}\n","/**\n * Truncates text to fit within a given width using binary search for optimal performance.\n * @param ctx The canvas rendering context used for text measurement\n * @param text The text to truncate\n * @param maxWidth The maximum width the text should occupy\n * @param ellipsis The ellipsis string to append (default: \"...\")\n * @returns The truncated text with ellipsis if needed\n */\nexport function truncateText(\n ctx: CanvasRenderingContext2D,\n text: string,\n maxWidth: number,\n ellipsis: string = '...'\n): string {\n const textWidth = ctx.measureText(text).width\n\n if (textWidth <= maxWidth || maxWidth <= 0) {\n return text\n }\n\n const ellipsisWidth = ctx.measureText(ellipsis).width\n const availableWidth = maxWidth - ellipsisWidth\n\n if (availableWidth <= 0) {\n return ellipsis\n }\n\n // Binary search for the right length\n let low = 0\n let high = text.length\n let bestFit = 0\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2)\n const testText = text.substring(0, mid)\n const testWidth = ctx.measureText(testText).width\n\n if (testWidth <= availableWidth) {\n bestFit = mid\n low = mid + 1\n } else {\n high = mid - 1\n }\n }\n\n return text.substring(0, bestFit) + ellipsis\n}\n","import { createI18n } from 'vue-i18n'\n\n// ESLint cannot statically resolve dynamic imports with relative paths in template strings,\n// but these are valid ES module imports that Vite processes correctly at build time.\n\n// Import only English locale eagerly as the default/fallback\nimport enCommands from './locales/en/commands.json' with { type: 'json' }\nimport en from './locales/en/main.json' with { type: 'json' }\nimport enNodes from './locales/en/nodeDefs.json' with { type: 'json' }\nimport enSettings from './locales/en/settings.json' with { type: 'json' }\n\nfunction buildLocale<\n M extends Record<string, unknown>,\n N extends Record<string, unknown>,\n C extends Record<string, unknown>,\n S extends Record<string, unknown>\n>(main: M, nodes: N, commands: C, settings: S) {\n return {\n ...main,\n nodeDefs: nodes,\n commands: commands,\n settings: settings\n } as M & { nodeDefs: N; commands: C; settings: S }\n}\n\n// Locale loader map - dynamically import locales only when needed\nconst localeLoaders: Record<\n string,\n () => Promise<{ default: Record<string, unknown> }>\n> = {\n ar: () => import('./locales/ar/main.json'),\n es: () => import('./locales/es/main.json'),\n fa: () => import('./locales/fa/main.json'),\n fr: () => import('./locales/fr/main.json'),\n ja: () => import('./locales/ja/main.json'),\n ko: () => import('./locales/ko/main.json'),\n ru: () => import('./locales/ru/main.json'),\n tr: () => import('./locales/tr/main.json'),\n zh: () => import('./locales/zh/main.json'),\n 'zh-TW': () => import('./locales/zh-TW/main.json'),\n 'pt-BR': () => import('./locales/pt-BR/main.json')\n}\n\nconst nodeDefsLoaders: Record<\n string,\n () => Promise<{ default: Record<string, unknown> }>\n> = {\n ar: () => import('./locales/ar/nodeDefs.json'),\n es: () => import('./locales/es/nodeDefs.json'),\n fa: () => import('./locales/fa/nodeDefs.json'),\n fr: () => import('./locales/fr/nodeDefs.json'),\n ja: () => import('./locales/ja/nodeDefs.json'),\n ko: () => import('./locales/ko/nodeDefs.json'),\n ru: () => import('./locales/ru/nodeDefs.json'),\n tr: () => import('./locales/tr/nodeDefs.json'),\n zh: () => import('./locales/zh/nodeDefs.json'),\n 'zh-TW': () => import('./locales/zh-TW/nodeDefs.json'),\n 'pt-BR': () => import('./locales/pt-BR/nodeDefs.json')\n}\n\nconst commandsLoaders: Record<\n string,\n () => Promise<{ default: Record<string, unknown> }>\n> = {\n ar: () => import('./locales/ar/commands.json'),\n es: () => import('./locales/es/commands.json'),\n fa: () => import('./locales/fa/commands.json'),\n fr: () => import('./locales/fr/commands.json'),\n ja: () => import('./locales/ja/commands.json'),\n ko: () => import('./locales/ko/commands.json'),\n ru: () => import('./locales/ru/commands.json'),\n tr: () => import('./locales/tr/commands.json'),\n zh: () => import('./locales/zh/commands.json'),\n 'zh-TW': () => import('./locales/zh-TW/commands.json'),\n 'pt-BR': () => import('./locales/pt-BR/commands.json')\n}\n\nconst settingsLoaders: Record<\n string,\n () => Promise<{ default: Record<string, unknown> }>\n> = {\n ar: () => import('./locales/ar/settings.json'),\n es: () => import('./locales/es/settings.json'),\n fa: () => import('./locales/fa/settings.json'),\n fr: () => import('./locales/fr/settings.json'),\n ja: () => import('./locales/ja/settings.json'),\n ko: () => import('./locales/ko/settings.json'),\n ru: () => import('./locales/ru/settings.json'),\n tr: () => import('./locales/tr/settings.json'),\n zh: () => import('./locales/zh/settings.json'),\n 'zh-TW': () => import('./locales/zh-TW/settings.json'),\n 'pt-BR': () => import('./locales/pt-BR/settings.json')\n}\n\n// Track which locales have been loaded\nconst loadedLocales = new Set<string>(['en'])\n\n// Track locales currently being loaded to prevent race conditions\nconst loadingLocales = new Map<string, Promise<void>>()\n\n// Store custom nodes i18n data for merging when locales are lazily loaded\nconst customNodesI18nData: Record<string, unknown> = {}\n\n/**\n * Dynamically load a locale and its associated files (nodeDefs, commands, settings)\n */\nexport async function loadLocale(locale: string): Promise<void> {\n if (loadedLocales.has(locale)) {\n return\n }\n\n // If already loading, return the existing promise to prevent duplicate loads\n const existingLoad = loadingLocales.get(locale)\n if (existingLoad) {\n return existingLoad\n }\n\n const loader = localeLoaders[locale]\n const nodeDefsLoader = nodeDefsLoaders[locale]\n const commandsLoader = commandsLoaders[locale]\n const settingsLoader = settingsLoaders[locale]\n\n if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) {\n console.warn(`Locale \"${locale}\" is not supported`)\n return\n }\n\n // Create and track the loading promise\n const loadPromise = (async () => {\n try {\n const [main, nodes, commands, settings] = await Promise.all([\n loader(),\n nodeDefsLoader(),\n commandsLoader(),\n settingsLoader()\n ])\n\n const messages = buildLocale(\n main.default,\n nodes.default,\n commands.default,\n settings.default\n )\n\n i18n.global.setLocaleMessage(locale, messages as LocaleMessages)\n loadedLocales.add(locale)\n\n if (customNodesI18nData[locale]) {\n i18n.global.mergeLocaleMessage(locale, customNodesI18nData[locale])\n }\n } catch (error) {\n console.error(`Failed to load locale \"${locale}\":`, error)\n throw error\n } finally {\n // Clean up the loading promise once complete\n loadingLocales.delete(locale)\n }\n })()\n\n loadingLocales.set(locale, loadPromise)\n return loadPromise\n}\n\n/**\n * Stores the data for later use when locales are lazily loaded,\n * and immediately merges data for already-loaded locales.\n */\nexport function mergeCustomNodesI18n(i18nData: Record<string, unknown>): void {\n // Clear existing data and replace with new data\n for (const key of Object.keys(customNodesI18nData)) {\n delete customNodesI18nData[key]\n }\n Object.assign(customNodesI18nData, i18nData)\n\n for (const [locale, message] of Object.entries(i18nData)) {\n if (loadedLocales.has(locale)) {\n i18n.global.mergeLocaleMessage(locale, message)\n }\n }\n}\n\n// Only include English in the initial bundle\nconst messages = {\n en: buildLocale(en, enNodes, enCommands, enSettings)\n}\n\n// Type for locale messages - inferred from the English locale structure\ntype LocaleMessages = typeof messages.en\n\nexport const i18n = createI18n({\n // Must set `false`, as Vue I18n Legacy API is for Vue 2\n legacy: false,\n locale: navigator.language.split('-')[0] || 'en',\n fallbackLocale: 'en',\n escapeParameter: true,\n messages,\n // Ignore warnings for locale options as each option is in its own language.\n // e.g. \"English\", \"中文\", \"Русский\", \"日本語\", \"한국어\", \"Français\", \"Español\"\n missingWarn: /^(?!settings\\.Comfy_Locale\\.options\\.).+/,\n fallbackWarn: /^(?!settings\\.Comfy_Locale\\.options\\.).+/\n})\n\n/** Convenience shorthand: i18n.global */\n/** @deprecated use useI18n */\nexport const { t, te, d } = i18n.global\n\n/**\n * Safe translation function that returns the fallback message if the key is not found.\n *\n * @param key - The key to translate.\n * @param fallbackMessage - The fallback message to use if the key is not found.\n */\nexport function st(key: string, fallbackMessage: string) {\n // The normal defaultMsg overload fails in some cases for custom nodes\n return te(key) ? t(key) : fallbackMessage\n}\n","import { t } from '@/i18n'\nimport { drawTextInArea } from '@/lib/litegraph/src/draw'\nimport { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'\nimport type { Point } from '@/lib/litegraph/src/interfaces'\nimport type {\n CanvasPointer,\n LGraphCanvas,\n LGraphNode,\n Size\n} from '@/lib/litegraph/src/litegraph'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'\nimport type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'\n\nexport interface DrawWidgetOptions {\n /** The width of the node where this widget will be displayed. */\n width: number\n /** Synonym for \"low quality\". */\n showText?: boolean\n}\n\ninterface DrawTruncatingTextOptions extends DrawWidgetOptions {\n /** The canvas context to draw the text on. */\n ctx: CanvasRenderingContext2D\n /** The amount of padding to add to the left of the text. */\n leftPadding?: number\n /** The amount of padding to add to the right of the text. */\n rightPadding?: number\n}\n\nexport interface WidgetEventOptions {\n e: CanvasPointerEvent\n node: LGraphNode\n canvas: LGraphCanvas\n}\n\nexport abstract class BaseWidget<\n TWidget extends IBaseWidget = IBaseWidget\n> implements IBaseWidget {\n /** From node edge to widget edge */\n static margin = 15\n /** From widget edge to tip of arrow button */\n static arrowMargin = 6\n /** Arrow button width */\n static arrowWidth = 10\n /** Absolute minimum display width of widget values */\n static minValueWidth = 42\n /** Minimum gap between label and value */\n static labelValueGap = 5\n\n declare computedHeight?: number\n declare serialize?: boolean\n computeLayoutSize?(node: LGraphNode): {\n minHeight: number\n maxHeight?: number\n minWidth: number\n maxWidth?: number\n }\n\n #node: LGraphNode\n /** The node that this widget belongs to. */\n get node() {\n return this.#node\n }\n\n linkedWidgets?: IBaseWidget[]\n name: string\n options: TWidget['options']\n label?: string\n type: TWidget['type']\n y: number = 0\n last_y?: number\n width?: number\n disabled?: boolean\n computedDisabled?: boolean\n hidden?: boolean\n advanced?: boolean\n promoted?: boolean\n tooltip?: string\n element?: HTMLElement\n callback?(\n value: TWidget['value'],\n canvas?: LGraphCanvas,\n node?: LGraphNode,\n pos?: Point,\n e?: CanvasPointerEvent\n ): void\n mouse?(\n event: CanvasPointerEvent,\n pointerOffset: Point,\n node: LGraphNode\n ): boolean\n computeSize?(width?: number): Size\n onPointerDown?(\n pointer: CanvasPointer,\n node: LGraphNode,\n canvas: LGraphCanvas\n ): boolean\n\n #value?: TWidget['value']\n get value(): TWidget['value'] {\n return this.#value\n }\n\n set value(value: TWidget['value']) {\n this.#value = value\n }\n\n constructor(widget: TWidget & { node: LGraphNode })\n constructor(widget: TWidget, node: LGraphNode)\n constructor(widget: TWidget & { node: LGraphNode }, node?: LGraphNode) {\n // Private fields\n this.#node = node ?? widget.node\n\n // The set and get functions for DOM widget values are hacked on to the options object;\n // attempting to set value before options will throw.\n // https://github.com/Comfy-Org/ComfyUI_frontend/blob/df86da3d672628a452baed3df3347a52c0c8d378/src/scripts/domWidget.ts#L125\n this.name = widget.name\n this.options = widget.options\n this.type = widget.type\n\n // `node` has no setter - Object.assign will throw.\n // TODO: Resolve this workaround. Ref: https://github.com/Comfy-Org/litegraph.js/issues/1022\n const {\n node: _,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n outline_color,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n background_color,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n height,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n text_color,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n secondary_text_color,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n disabledTextColor,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n displayName,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n displayValue,\n // @ts-expect-error Prevent naming conflicts with custom nodes.\n labelBaseline,\n promoted,\n ...safeValues\n } = widget\n\n Object.assign(this, safeValues)\n }\n\n get outline_color() {\n if (this.promoted) return LiteGraph.WIDGET_PROMOTED_OUTLINE_COLOR\n return this.advanced\n ? LiteGraph.WIDGET_ADVANCED_OUTLINE_COLOR\n : LiteGraph.WIDGET_OUTLINE_COLOR\n }\n\n get background_color() {\n return LiteGraph.WIDGET_BGCOLOR\n }\n\n get height() {\n return LiteGraph.NODE_WIDGET_HEIGHT\n }\n\n get text_color() {\n return LiteGraph.WIDGET_TEXT_COLOR\n }\n\n get secondary_text_color() {\n return LiteGraph.WIDGET_SECONDARY_TEXT_COLOR\n }\n\n get disabledTextColor() {\n return LiteGraph.WIDGET_DISABLED_TEXT_COLOR\n }\n\n get displayName() {\n return this.label || this.name\n }\n\n // TODO: Resolve this workaround. Ref: https://github.com/Comfy-Org/litegraph.js/issues/1022\n get _displayValue(): string {\n return this.computedDisabled ? '' : String(this.value)\n }\n\n get labelBaseline() {\n return this.y + this.height * 0.7\n }\n\n /**\n * Draws the widget\n * @param ctx The canvas context\n * @param options The options for drawing the widget\n * @remarks Not naming this `draw` as `draw` conflicts with the `draw` method in\n * custom widgets.\n */\n abstract drawWidget(\n ctx: CanvasRenderingContext2D,\n options: DrawWidgetOptions\n ): void\n\n /**\n * Draws the standard widget shape - elongated capsule. The path of the widget shape is not\n * cleared, and may be used for further drawing.\n * @param ctx The canvas context\n * @param options The options for drawing the widget\n * @remarks Leaves {@link ctx} dirty.\n */\n protected drawWidgetShape(\n ctx: CanvasRenderingContext2D,\n { width, showText }: DrawWidgetOptions\n ): void {\n const { height, y } = this\n const { margin } = BaseWidget\n\n ctx.textAlign = 'left'\n ctx.strokeStyle = this.outline_color\n ctx.fillStyle = this.background_color\n ctx.beginPath()\n\n if (showText) {\n ctx.roundRect(margin, y, width - margin * 2, height, [height * 0.5])\n } else {\n ctx.rect(margin, y, width - margin * 2, height)\n }\n ctx.fill()\n if (showText && !this.computedDisabled) ctx.stroke()\n }\n\n /**\n * Draws a placeholder for widgets that only have a Vue implementation.\n * @param ctx The canvas context\n * @param options The options for drawing the widget\n * @param label The label to display (e.g., \"ImageCrop\", \"BoundingBox\")\n */\n protected drawVueOnlyWarning(\n ctx: CanvasRenderingContext2D,\n { width }: DrawWidgetOptions,\n label: string\n ): void {\n const { y, height } = this\n\n ctx.save()\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n ctx.fillText(\n `${label}: ${t('widgets.node2only')}`,\n width / 2,\n y + height / 2\n )\n\n ctx.restore()\n }\n\n /**\n * A shared routine for drawing a label and value as text, truncated\n * if they exceed the available width.\n */\n protected drawTruncatingText({\n ctx,\n width,\n leftPadding = 5,\n rightPadding = 20\n }: DrawTruncatingTextOptions): void {\n const { height, y } = this\n const { margin } = BaseWidget\n\n // Measure label and value\n const { displayName, _displayValue } = this\n const labelWidth = ctx.measureText(displayName).width\n const valueWidth = ctx.measureText(_displayValue).width\n\n const gap = BaseWidget.labelValueGap\n const x = margin * 2 + leftPadding\n\n const totalWidth = width - x - 2 * margin - rightPadding\n const requiredWidth = labelWidth + gap + valueWidth\n\n const area = new Rectangle(x, y, totalWidth, height * 0.7)\n\n ctx.fillStyle = this.secondary_text_color\n\n if (requiredWidth <= totalWidth) {\n // Draw label & value normally\n drawTextInArea({ ctx, text: displayName, area, align: 'left' })\n } else if (LiteGraph.truncateWidgetTextEvenly) {\n // Label + value will not fit - scale evenly to fit\n const scale = (totalWidth - gap) / (requiredWidth - gap)\n area.width = labelWidth * scale\n\n drawTextInArea({ ctx, text: displayName, area, align: 'left' })\n\n // Move the area to the right to render the value\n area.right = x + totalWidth\n area.setWidthRightAnchored(valueWidth * scale)\n } else if (LiteGraph.truncateWidgetValuesFirst) {\n // Label + value will not fit - use legacy scaling of value first\n const cappedLabelWidth = Math.min(labelWidth, totalWidth)\n\n area.width = cappedLabelWidth\n drawTextInArea({ ctx, text: displayName, area, align: 'left' })\n\n area.right = x + totalWidth\n area.setWidthRightAnchored(\n Math.max(totalWidth - gap - cappedLabelWidth, 0)\n )\n } else {\n // Label + value will not fit - scale label first\n const cappedValueWidth = Math.min(valueWidth, totalWidth)\n\n area.width = Math.max(totalWidth - gap - cappedValueWidth, 0)\n drawTextInArea({ ctx, text: displayName, area, align: 'left' })\n\n area.right = x + totalWidth\n area.setWidthRightAnchored(cappedValueWidth)\n }\n ctx.fillStyle = this.text_color\n drawTextInArea({ ctx, text: _displayValue, area, align: 'right' })\n }\n\n /**\n * Handles the click event for the widget\n * @param options The options for handling the click event\n */\n abstract onClick(options: WidgetEventOptions): void\n\n /**\n * Handles the drag event for the widget\n * @param options The options for handling the drag event\n */\n onDrag?(options: WidgetEventOptions): void\n\n /**\n * Sets the value of the widget\n * @param value The value to set\n * @param options The options for setting the value\n */\n setValue(\n value: TWidget['value'],\n { e, node, canvas }: WidgetEventOptions\n ): void {\n const oldValue = this.value\n if (value === this.value) return\n\n const v = this.type === 'number' ? Number(value) : value\n this.value = v\n if (\n this.options?.property &&\n node.properties[this.options.property] !== undefined\n ) {\n node.setProperty(this.options.property, v)\n }\n const pos = canvas.graph_mouse\n this.callback?.(this.value, canvas, node, pos, e)\n\n node.onWidgetChanged?.(this.name ?? '', v, oldValue, this)\n if (node.graph) node.graph._version++\n }\n\n /**\n * Clones the widget.\n * @param node The node that will own the cloned widget.\n * @returns A new widget with the same properties as the original\n * @remarks Subclasses with custom constructors must override this method.\n *\n * Correctly and safely typing this is currently not possible (practical?) in TypeScript 5.8.\n */\n createCopyForNode(node: LGraphNode): this {\n // @ts-expect-error - Constructor type casting for widget cloning\n const cloned: this = new (this.constructor as typeof this)(this, node)\n cloned.value = this.value\n return cloned\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { IAssetWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions } from './BaseWidget'\n\nexport class AssetWidget\n extends BaseWidget<IAssetWidget>\n implements IAssetWidget\n{\n constructor(widget: IAssetWidget, node: LGraphNode) {\n super(widget, node)\n this.type ??= 'asset'\n this.value = widget.value?.toString() ?? ''\n }\n\n override set value(value: IAssetWidget['value']) {\n const oldValue = this.value\n super.value = value\n\n // Force canvas redraw when value changes to show update immediately\n if (oldValue !== value && this.node.graph?.list_of_graphcanvas) {\n for (const canvas of this.node.graph.list_of_graphcanvas) {\n canvas.setDirty(true)\n }\n }\n }\n\n override get value(): IAssetWidget['value'] {\n return super.value\n }\n\n override get _displayValue(): string {\n return String(this.value) //FIXME: Resolve asset name\n }\n\n override drawWidget(\n ctx: CanvasRenderingContext2D,\n { width, showText = true }: DrawWidgetOptions\n ) {\n // Store original context attributes\n const { fillStyle, strokeStyle, textAlign } = ctx\n\n this.drawWidgetShape(ctx, { width, showText })\n\n if (showText) {\n this.drawTruncatingText({ ctx, width, leftPadding: 0, rightPadding: 0 })\n }\n\n // Restore original context attributes\n Object.assign(ctx, { textAlign, strokeStyle, fillStyle })\n }\n\n override onClick() {\n //Open Modal\n this.options.openModal(this)\n }\n}\n","import type { IBooleanWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\nexport class BooleanWidget\n extends BaseWidget<IBooleanWidget>\n implements IBooleanWidget\n{\n override type = 'toggle' as const\n\n override drawWidget(\n ctx: CanvasRenderingContext2D,\n { width, showText = true }: DrawWidgetOptions\n ) {\n const { height, y } = this\n const { margin } = BaseWidget\n\n this.drawWidgetShape(ctx, { width, showText })\n\n ctx.fillStyle = this.value ? '#89A' : '#333'\n ctx.beginPath()\n ctx.arc(width - margin * 2, y + height * 0.5, height * 0.36, 0, Math.PI * 2)\n ctx.fill()\n\n if (showText) {\n this.drawLabel(ctx, margin * 2)\n this.drawValue(ctx, width - 40)\n }\n }\n\n drawLabel(ctx: CanvasRenderingContext2D, x: number): void {\n // Draw label\n ctx.fillStyle = this.secondary_text_color\n const { displayName } = this\n if (displayName) ctx.fillText(displayName, x, this.labelBaseline)\n }\n\n drawValue(ctx: CanvasRenderingContext2D, x: number): void {\n // Draw value\n ctx.fillStyle = this.value ? this.text_color : this.secondary_text_color\n ctx.textAlign = 'right'\n const value = this.value\n ? this.options.on || 'true'\n : this.options.off || 'false'\n ctx.fillText(value, x, this.labelBaseline)\n }\n\n override onClick(options: WidgetEventOptions) {\n this.setValue(!this.value, options)\n }\n}\n","import type { IBoundingBoxWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for defining bounding box regions.\n * This widget only has a Vue implementation.\n */\nexport class BoundingBoxWidget\n extends BaseWidget<IBoundingBoxWidget>\n implements IBoundingBoxWidget\n{\n override type = 'boundingbox' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n this.drawVueOnlyWarning(ctx, options, 'BoundingBox')\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This widget only has a Vue implementation\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { IButtonWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\nexport class ButtonWidget\n extends BaseWidget<IButtonWidget>\n implements IButtonWidget\n{\n override type = 'button' as const\n clicked: boolean\n\n constructor(widget: IButtonWidget, node: LGraphNode) {\n super(widget, node)\n this.clicked ??= false\n }\n\n /**\n * Draws the widget\n * @param ctx The canvas context\n * @param options The options for drawing the widget\n */\n override drawWidget(\n ctx: CanvasRenderingContext2D,\n { width, showText = true }: DrawWidgetOptions\n ) {\n // Store original context attributes\n const { fillStyle, strokeStyle, textAlign } = ctx\n\n const { height, y } = this\n const { margin } = BaseWidget\n\n // Draw button background\n ctx.fillStyle = this.background_color\n if (this.clicked) {\n ctx.fillStyle = '#AAA'\n this.clicked = false\n }\n ctx.fillRect(margin, y, width - margin * 2, height)\n\n // Draw button outline if not disabled\n if (showText && !this.computedDisabled) {\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(margin, y, width - margin * 2, height)\n }\n\n // Draw button text\n if (showText) this.drawLabel(ctx, width * 0.5)\n\n // Restore original context attributes\n Object.assign(ctx, { textAlign, strokeStyle, fillStyle })\n }\n\n drawLabel(ctx: CanvasRenderingContext2D, x: number): void {\n ctx.textAlign = 'center'\n ctx.fillStyle = this.text_color\n ctx.fillText(this.displayName, x, this.y + this.height * 0.7)\n }\n\n override onClick({ e, node, canvas }: WidgetEventOptions) {\n const pos = canvas.graph_mouse\n\n // Set clicked state and mark canvas as dirty\n this.clicked = true\n canvas.setDirty(true)\n\n // Call the callback with widget value\n this.callback?.(this.value, canvas, node, pos, e)\n }\n}\n","import { t } from '@/i18n'\n\nimport type { IChartWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for displaying charts and data visualizations\n * This is a widget that only has a Vue widgets implementation\n */\nexport class ChartWidget\n extends BaseWidget<IChartWidget>\n implements IChartWidget\n{\n override type = 'chart' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `Chart: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import { t } from '@/i18n'\n\nimport type { IColorWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for displaying a color picker\n * This is a widget that only has a Vue widgets implementation\n */\nexport class ColorWidget\n extends BaseWidget<IColorWidget>\n implements IColorWidget\n{\n override type = 'color' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `Color: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Base class for widgets that have increment and decrement buttons.\n */\nexport abstract class BaseSteppedWidget<\n TWidget extends IBaseWidget = IBaseWidget\n> extends BaseWidget<TWidget> {\n /**\n * Whether the widget can increment its value\n * @returns `true` if the widget can increment its value, otherwise `false`\n */\n abstract canIncrement(): boolean\n /**\n * Whether the widget can decrement its value\n * @returns `true` if the widget can decrement its value, otherwise `false`\n */\n abstract canDecrement(): boolean\n /**\n * Increment the value of the widget\n * @param options The options for the widget event\n */\n abstract incrementValue(options: WidgetEventOptions): void\n /**\n * Decrement the value of the widget\n * @param options The options for the widget event\n */\n abstract decrementValue(options: WidgetEventOptions): void\n\n /**\n * Draw the arrow buttons for the widget\n * @param ctx The canvas rendering context\n * @param width The width of the widget\n */\n drawArrowButtons(ctx: CanvasRenderingContext2D, width: number) {\n const { height, text_color, disabledTextColor, y } = this\n const { arrowMargin, arrowWidth, margin } = BaseWidget\n const arrowTipX = margin + arrowMargin\n const arrowInnerX = arrowTipX + arrowWidth\n\n // Draw left arrow\n ctx.fillStyle = this.canDecrement() ? text_color : disabledTextColor\n ctx.beginPath()\n ctx.moveTo(arrowInnerX, y + 5)\n ctx.lineTo(arrowTipX, y + height * 0.5)\n ctx.lineTo(arrowInnerX, y + height - 5)\n ctx.fill()\n\n // Draw right arrow\n ctx.fillStyle = this.canIncrement() ? text_color : disabledTextColor\n ctx.beginPath()\n ctx.moveTo(width - arrowInnerX, y + 5)\n ctx.lineTo(width - arrowTipX, y + height * 0.5)\n ctx.lineTo(width - arrowInnerX, y + height - 5)\n ctx.fill()\n }\n\n override drawWidget(\n ctx: CanvasRenderingContext2D,\n options: DrawWidgetOptions\n ) {\n // Store original context attributes\n const { fillStyle, strokeStyle, textAlign } = ctx\n\n this.drawWidgetShape(ctx, options)\n if (options.showText) {\n if (!this.computedDisabled) this.drawArrowButtons(ctx, options.width)\n\n this.drawTruncatingText({ ctx, width: options.width })\n }\n\n // Restore original context attributes\n Object.assign(ctx, { textAlign, strokeStyle, fillStyle })\n }\n}\n","import { clamp } from 'es-toolkit/compat'\n\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport type {\n IComboWidget,\n IStringComboWidget\n} from '@/lib/litegraph/src/types/widgets'\nimport { warnDeprecated } from '@/lib/litegraph/src/utils/feedback'\n\nimport { BaseSteppedWidget } from './BaseSteppedWidget'\nimport type { WidgetEventOptions } from './BaseWidget'\n\n/**\n * This is used as an (invalid) assertion to resolve issues with legacy duck-typed values.\n *\n * Function style in use by:\n * https://github.com/kijai/ComfyUI-KJNodes/blob/c3dc82108a2a86c17094107ead61d63f8c76200e/web/js/setgetnodes.js#L401-L404\n */\ntype Values =\n | string[]\n | Record<string, string>\n | ((widget?: ComboWidget, node?: LGraphNode) => string[])\n\nfunction toArray(values: Values): string[] {\n return Array.isArray(values) ? values : Object.keys(values)\n}\n\nexport class ComboWidget\n extends BaseSteppedWidget<IStringComboWidget | IComboWidget>\n implements IComboWidget\n{\n override type = 'combo' as const\n\n override get _displayValue() {\n if (this.computedDisabled) return ''\n\n if (this.options.getOptionLabel) {\n try {\n return this.options.getOptionLabel(\n this.value ? String(this.value) : null\n )\n } catch (e) {\n console.error('Failed to map value:', e)\n return this.value ? String(this.value) : ''\n }\n }\n\n const { values: rawValues } = this.options\n if (rawValues) {\n const values = typeof rawValues === 'function' ? rawValues() : rawValues\n\n if (values && !Array.isArray(values)) {\n return values[this.value]\n }\n }\n return typeof this.value === 'number' ? String(this.value) : this.value\n }\n\n private getValues(node: LGraphNode): Values {\n const { values } = this.options\n if (values == null) throw new Error('[ComboWidget]: values is required')\n\n return typeof values === 'function' ? values(this, node) : values\n }\n\n /**\n * Checks if the value is {@link Array.at at} the given index in the combo list.\n * @param increment `true` if checking the use of the increment button, `false` for decrement\n * @returns `true` if the value is at the given index, otherwise `false`.\n */\n private canUseButton(increment: boolean): boolean {\n const { values } = this.options\n // If using legacy duck-typed method, false is the most permissive return value\n if (typeof values === 'function') return false\n\n const valuesArray = toArray(values)\n if (!(valuesArray.length > 1)) return false\n\n // Edge case where the value is both the first and last item in the list\n const firstValue = valuesArray.at(0)\n const lastValue = valuesArray.at(-1)\n if (firstValue === lastValue) return true\n\n return this.value !== (increment ? lastValue : firstValue)\n }\n\n /**\n * Returns `true` if the current value is not the last value in the list.\n * Handles edge case where the value is both the first and last item in the list.\n */\n override canIncrement(): boolean {\n return this.canUseButton(true)\n }\n\n override canDecrement(): boolean {\n return this.canUseButton(false)\n }\n\n override incrementValue(options: WidgetEventOptions): void {\n this.tryChangeValue(1, options)\n }\n\n override decrementValue(options: WidgetEventOptions): void {\n this.tryChangeValue(-1, options)\n }\n\n private tryChangeValue(delta: number, options: WidgetEventOptions): void {\n const values = this.getValues(options.node)\n const indexedValues = toArray(values)\n\n // avoids double click event\n options.canvas.last_mouseclick = 0\n\n const foundIndex =\n typeof values === 'object'\n ? indexedValues.indexOf(String(this.value)) + delta\n : // @ts-expect-error handle non-string values\n indexedValues.indexOf(this.value) + delta\n\n const index = clamp(foundIndex, 0, indexedValues.length - 1)\n\n const value = Array.isArray(values) ? values[index] : index\n this.setValue(value, options)\n }\n\n override onClick({ e, node, canvas }: WidgetEventOptions) {\n const x = e.canvasX - node.pos[0]\n const width = this.width || node.size[0]\n\n // Deprecated functionality (warning as of v0.14.5)\n if (typeof this.options.values === 'function') {\n warnDeprecated(\n 'Using a function for values is deprecated. Use an array of unique values instead.'\n )\n }\n\n // Determine if clicked on left/right arrows\n if (x < 40) return this.decrementValue({ e, node, canvas })\n if (x > width - 40) return this.incrementValue({ e, node, canvas })\n\n // Otherwise, show dropdown menu\n const values = this.getValues(node)\n const values_list = toArray(values)\n\n // Use addItem to solve duplicate filename issues\n if (this.options.getOptionLabel) {\n const menuOptions = {\n scale: Math.max(1, canvas.ds.scale),\n event: e,\n className: 'dark',\n callback: (value: string) => {\n this.setValue(value, { e, node, canvas })\n }\n }\n const menu = new LiteGraph.ContextMenu([], menuOptions)\n\n for (const value of values_list) {\n try {\n const label = this.options.getOptionLabel(String(value))\n menu.addItem(label, value, menuOptions)\n } catch (err) {\n console.error('Failed to map value:', err)\n menu.addItem(String(value), value, menuOptions)\n }\n }\n return\n }\n\n // Show dropdown menu when user clicks on widget label\n const text_values = values != values_list ? Object.values(values) : values\n new LiteGraph.ContextMenu(text_values, {\n scale: Math.max(1, canvas.ds.scale),\n event: e,\n className: 'dark',\n callback: (value: string) => {\n this.setValue(\n values != values_list ? text_values.indexOf(value) : value,\n { e, node, canvas }\n )\n }\n })\n }\n}\n","import { t } from '@/i18n'\n\nimport type { IFileUploadWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for handling file uploads\n * This is a widget that only has a Vue widgets implementation\n */\nexport class FileUploadWidget\n extends BaseWidget<IFileUploadWidget>\n implements IFileUploadWidget\n{\n override type = 'fileupload' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `Fileupload: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import { t } from '@/i18n'\n\nimport type { IGalleriaWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for displaying image galleries\n * This is a widget that only has a Vue widgets implementation\n */\nexport class GalleriaWidget\n extends BaseWidget<IGalleriaWidget>\n implements IGalleriaWidget\n{\n override type = 'galleria' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `Galleria: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import { t } from '@/i18n'\n\nimport type { IImageCompareWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for comparing two images side by side\n * This is a widget that only has a Vue widgets implementation\n */\nexport class ImageCompareWidget\n extends BaseWidget<IImageCompareWidget>\n implements IImageCompareWidget\n{\n override type = 'imagecompare' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `ImageCompare: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import type { IImageCropWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for displaying an image crop preview.\n * This widget only has a Vue implementation.\n */\nexport class ImageCropWidget\n extends BaseWidget<IImageCropWidget>\n implements IImageCropWidget\n{\n override type = 'imagecrop' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n this.drawVueOnlyWarning(ctx, options, 'ImageCrop')\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This widget only has a Vue implementation\n }\n}\n","import { clamp } from 'es-toolkit/compat'\n\nimport type { IKnobWidget } from '@/lib/litegraph/src/types/widgets'\nimport { getWidgetStep } from '@/lib/litegraph/src/utils/widget'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\nexport class KnobWidget extends BaseWidget<IKnobWidget> implements IKnobWidget {\n override type = 'knob' as const\n\n /**\n * Compute the layout size of the widget.\n * @returns The layout size of the widget.\n */\n override computeLayoutSize(): {\n minHeight: number\n maxHeight?: number\n minWidth: number\n maxWidth?: number\n } {\n return {\n minHeight: 60,\n minWidth: 20,\n maxHeight: 1_000_000,\n maxWidth: 1_000_000\n }\n }\n\n override get height(): number {\n return this.computedHeight || super.height\n }\n\n drawWidget(\n ctx: CanvasRenderingContext2D,\n { width, showText = true }: DrawWidgetOptions\n ): void {\n // Store original context attributes\n const { fillStyle, strokeStyle, textAlign } = ctx\n\n const { y } = this\n const { margin } = BaseWidget\n\n const { gradient_stops = 'rgb(14, 182, 201); rgb(0, 216, 72)' } =\n this.options\n const effective_height = this.computedHeight || this.height\n // Draw background\n const size_modifier =\n Math.min(this.computedHeight || this.height, this.width || 20) / 20 // TODO: replace magic numbers\n const arc_center = { x: width / 2, y: effective_height / 2 + y }\n ctx.lineWidth =\n (Math.min(width, effective_height) - margin * size_modifier) / 6\n const arc_size =\n (Math.min(width, effective_height) -\n margin * size_modifier -\n ctx.lineWidth) /\n 2\n {\n const gradient = ctx.createRadialGradient(\n arc_center.x,\n arc_center.y,\n arc_size + ctx.lineWidth,\n 0,\n 0,\n arc_size + ctx.lineWidth\n )\n gradient.addColorStop(0, 'rgb(29, 29, 29)')\n gradient.addColorStop(1, 'rgb(116, 116, 116)')\n ctx.fillStyle = gradient\n }\n ctx.beginPath()\n\n {\n ctx.arc(\n arc_center.x,\n arc_center.y,\n arc_size + ctx.lineWidth / 2,\n 0,\n Math.PI * 2,\n false\n )\n ctx.fill()\n ctx.closePath()\n }\n\n // Draw knob's background\n const arc = {\n start_angle: Math.PI * 0.6,\n end_angle: Math.PI * 2.4\n }\n ctx.beginPath()\n {\n const gradient = ctx.createRadialGradient(\n arc_center.x,\n arc_center.y,\n arc_size + ctx.lineWidth,\n 0,\n 0,\n arc_size + ctx.lineWidth\n )\n gradient.addColorStop(0, 'rgb(99, 99, 99)')\n gradient.addColorStop(1, 'rgb(36, 36, 36)')\n ctx.strokeStyle = gradient\n }\n ctx.arc(\n arc_center.x,\n arc_center.y,\n arc_size,\n arc.start_angle,\n arc.end_angle,\n false\n )\n ctx.stroke()\n ctx.closePath()\n\n const range = this.options.max - this.options.min\n let nvalue = (this.value - this.options.min) / range\n nvalue = clamp(nvalue, 0, 1)\n\n // Draw value\n ctx.beginPath()\n const gradient = ctx.createConicGradient(\n arc.start_angle,\n arc_center.x,\n arc_center.y\n )\n const gs = gradient_stops.split(';')\n for (const [index, stop] of gs.entries()) {\n gradient.addColorStop(index, stop.trim())\n }\n\n ctx.strokeStyle = gradient\n const value_end_angle =\n (arc.end_angle - arc.start_angle) * nvalue + arc.start_angle\n ctx.arc(\n arc_center.x,\n arc_center.y,\n arc_size,\n arc.start_angle,\n value_end_angle,\n false\n )\n ctx.stroke()\n ctx.closePath()\n\n // Draw outline if not disabled\n if (showText && !this.computedDisabled) {\n ctx.strokeStyle = this.outline_color\n // Draw value\n ctx.beginPath()\n ctx.strokeStyle = this.outline_color\n ctx.arc(\n arc_center.x,\n arc_center.y,\n arc_size + ctx.lineWidth / 2,\n 0,\n Math.PI * 2,\n false\n )\n ctx.lineWidth = 1\n ctx.stroke()\n ctx.closePath()\n }\n\n // Draw marker if present\n // TODO: TBD later when options work\n\n // Draw text\n if (showText) {\n ctx.textAlign = 'center'\n ctx.fillStyle = this.text_color\n const fixedValue = Number(this.value).toFixed(this.options.precision ?? 3)\n ctx.fillText(\n `${this.label || this.name}\\n${fixedValue}`,\n width * 0.5,\n y + effective_height * 0.5\n )\n }\n\n // Restore original context attributes\n Object.assign(ctx, { textAlign, strokeStyle, fillStyle })\n }\n\n onClick(): void {\n this.current_drag_offset = 0\n }\n\n current_drag_offset = 0\n override onDrag(options: WidgetEventOptions): void {\n if (this.options.read_only) return\n const { e } = options\n const step = getWidgetStep(this.options)\n // Shift to move by 10% increments\n const range = this.options.max - this.options.min\n const range_10_percent = range / 10\n const range_1_percent = range / 100\n const step_for = {\n delta_x: step,\n shift:\n range_10_percent > step\n ? range_10_percent - (range_10_percent % step)\n : step,\n delta_y:\n range_1_percent > step\n ? range_1_percent - (range_1_percent % step)\n : step // 1% increments\n }\n\n const use_y = Math.abs(e.movementY) > Math.abs(e.movementX)\n const delta = use_y ? -e.movementY : e.movementX // Y is inverted so that UP increases the value\n const drag_threshold = 15\n // Calculate new value based on drag movement\n this.current_drag_offset += delta\n let adjustment = 0\n if (this.current_drag_offset > drag_threshold) {\n adjustment += 1\n this.current_drag_offset -= drag_threshold\n } else if (this.current_drag_offset < -drag_threshold) {\n adjustment -= 1\n this.current_drag_offset += drag_threshold\n }\n\n const step_with_shift_modifier = e.shiftKey\n ? step_for.shift\n : use_y\n ? step_for.delta_y\n : step\n\n const deltaValue = adjustment * step_with_shift_modifier\n const newValue = clamp(\n this.value + deltaValue,\n this.options.min,\n this.options.max\n )\n if (newValue !== this.value) {\n this.setValue(newValue, options)\n }\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions } from './BaseWidget'\n\n/**\n * Wraps a legacy POJO custom widget, so that all widgets may be called via the same internal interface.\n *\n * Support will eventually be removed.\n * @remarks Expect this class to undergo breaking changes without warning.\n */\nexport class LegacyWidget<TWidget extends IBaseWidget = IBaseWidget>\n extends BaseWidget<TWidget>\n implements IBaseWidget\n{\n override draw?(\n ctx: CanvasRenderingContext2D,\n node: LGraphNode,\n widget_width: number,\n y: number,\n H: number,\n lowQuality?: boolean\n ): void\n\n override drawWidget(\n ctx: CanvasRenderingContext2D,\n options: DrawWidgetOptions\n ) {\n const H = LiteGraph.NODE_WIDGET_HEIGHT\n this.draw?.(ctx, this.node, options.width, this.y, H, !!options.showText)\n }\n\n override onClick() {\n console.warn(\n 'Custom widget wrapper onClick was just called. Handling for third party widgets is done via LGraphCanvas - the mouse callback.'\n )\n }\n}\n","import { t } from '@/i18n'\n\nimport type { IMarkdownWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for displaying markdown formatted text\n * This is a widget that only has a Vue widgets implementation\n */\nexport class MarkdownWidget\n extends BaseWidget<IMarkdownWidget>\n implements IMarkdownWidget\n{\n override type = 'markdown' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `Markdown: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import { t } from '@/i18n'\n\nimport type { IMultiSelectWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for selecting multiple options\n * This is a widget that only has a Vue widgets implementation\n */\nexport class MultiSelectWidget\n extends BaseWidget<IMultiSelectWidget>\n implements IMultiSelectWidget\n{\n override type = 'multiselect' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `MultiSelect: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import type { INumericWidget } from '@/lib/litegraph/src/types/widgets'\nimport { evaluateInput, getWidgetStep } from '@/lib/litegraph/src/utils/widget'\n\nimport { BaseSteppedWidget } from './BaseSteppedWidget'\nimport type { WidgetEventOptions } from './BaseWidget'\n\nexport class NumberWidget\n extends BaseSteppedWidget<INumericWidget>\n implements INumericWidget\n{\n override type = 'number' as const\n\n override get _displayValue() {\n if (this.computedDisabled) return ''\n return Number(this.value).toFixed(\n this.options.precision !== undefined ? this.options.precision : 3\n )\n }\n\n override canIncrement(): boolean {\n const { max } = this.options\n return max == null || this.value < max\n }\n\n override canDecrement(): boolean {\n const { min } = this.options\n return min == null || this.value > min\n }\n\n override incrementValue(options: WidgetEventOptions): void {\n this.setValue(this.value + getWidgetStep(this.options), options)\n }\n\n override decrementValue(options: WidgetEventOptions): void {\n this.setValue(this.value - getWidgetStep(this.options), options)\n }\n\n override setValue(value: number, options: WidgetEventOptions) {\n let newValue = value\n if (this.options.min != null && newValue < this.options.min) {\n newValue = this.options.min\n }\n if (this.options.max != null && newValue > this.options.max) {\n newValue = this.options.max\n }\n super.setValue(newValue, options)\n }\n\n override onClick({ e, node, canvas }: WidgetEventOptions) {\n const x = e.canvasX - node.pos[0]\n const width = this.width || node.size[0]\n\n // Determine if clicked on left/right arrows\n const delta = x < 40 ? -1 : x > width - 40 ? 1 : 0\n\n if (delta) {\n // Handle left/right arrow clicks\n this.setValue(this.value + delta * getWidgetStep(this.options), {\n e,\n node,\n canvas\n })\n return\n }\n\n // Handle center click - show prompt\n canvas.prompt(\n 'Value',\n this.value,\n (v: string) => {\n const parsed = evaluateInput(v)\n if (parsed !== undefined) this.setValue(parsed, { e, node, canvas })\n },\n e\n )\n }\n\n /**\n * Handles drag events for the number widget\n * @param options The options for handling the drag event\n */\n override onDrag({ e, node, canvas }: WidgetEventOptions) {\n const width = this.width || node.width\n const x = e.canvasX - node.pos[0]\n const delta = x < 40 ? -1 : x > width - 40 ? 1 : 0\n\n if (delta && x > -3 && x < width + 3) return\n this.setValue(this.value + (e.deltaX ?? 0) * getWidgetStep(this.options), {\n e,\n node,\n canvas\n })\n }\n}\n","import { t } from '@/i18n'\n\nimport type { ISelectButtonWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for selecting from a group of buttons\n * This is a widget that only has a Vue widgets implementation\n */\nexport class SelectButtonWidget\n extends BaseWidget<ISelectButtonWidget>\n implements ISelectButtonWidget\n{\n override type = 'selectbutton' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n const { width } = options\n const { y, height } = this\n\n const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx\n\n ctx.fillStyle = this.background_color\n ctx.fillRect(15, y, width - 30, height)\n\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(15, y, width - 30, height)\n\n ctx.fillStyle = this.text_color\n ctx.font = '11px monospace'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n const text = `SelectButton: ${t('widgets.node2only')}`\n ctx.fillText(text, width / 2, y + height / 2)\n\n Object.assign(ctx, {\n fillStyle,\n strokeStyle,\n textAlign,\n textBaseline,\n font\n })\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This is a widget that only has a Vue widgets implementation\n }\n}\n","import { clamp } from 'es-toolkit/compat'\n\nimport type { ISliderWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\nexport class SliderWidget\n extends BaseWidget<ISliderWidget>\n implements ISliderWidget\n{\n override type = 'slider' as const\n\n marker?: number\n\n /**\n * Draws the widget\n * @param ctx The canvas context\n * @param options The options for drawing the widget\n */\n override drawWidget(\n ctx: CanvasRenderingContext2D,\n { width, showText = true }: DrawWidgetOptions\n ) {\n // Store original context attributes\n const { fillStyle, strokeStyle, textAlign } = ctx\n\n const { height, y } = this\n const { margin } = BaseWidget\n\n // Draw background\n ctx.fillStyle = this.background_color\n ctx.fillRect(margin, y, width - margin * 2, height)\n\n // Calculate normalized value\n const range = this.options.max - this.options.min\n let nvalue = (this.value - this.options.min) / range\n nvalue = clamp(nvalue, 0, 1)\n\n // Draw slider bar\n ctx.fillStyle = this.options.slider_color ?? '#678'\n ctx.fillRect(margin, y, nvalue * (width - margin * 2), height)\n\n // Draw outline if not disabled\n if (showText && !this.computedDisabled) {\n ctx.strokeStyle = this.outline_color\n ctx.strokeRect(margin, y, width - margin * 2, height)\n }\n\n // Draw marker if present\n if (this.marker != null) {\n let marker_nvalue = (this.marker - this.options.min) / range\n marker_nvalue = clamp(marker_nvalue, 0, 1)\n ctx.fillStyle = this.options.marker_color ?? '#AA9'\n ctx.fillRect(margin + marker_nvalue * (width - margin * 2), y, 2, height)\n }\n\n // Draw text\n if (showText) {\n ctx.textAlign = 'center'\n ctx.fillStyle = this.text_color\n const fixedValue = Number(this.value).toFixed(this.options.precision ?? 3)\n ctx.fillText(\n `${this.label || this.name} ${fixedValue}`,\n width * 0.5,\n y + height * 0.7\n )\n }\n\n // Restore original context attributes\n Object.assign(ctx, { textAlign, strokeStyle, fillStyle })\n }\n\n /**\n * Handles click events for the slider widget\n */\n override onClick(options: WidgetEventOptions) {\n if (this.options.read_only) return\n\n const { e, node } = options\n const width = this.width || node.size[0]\n const x = e.canvasX - node.pos[0]\n\n // Calculate new value based on click position\n const slideFactor = clamp((x - 15) / (width - 30), 0, 1)\n const newValue =\n this.options.min + (this.options.max - this.options.min) * slideFactor\n\n if (newValue !== this.value) {\n this.setValue(newValue, options)\n }\n }\n\n /**\n * Handles drag events for the slider widget\n */\n override onDrag(options: WidgetEventOptions) {\n if (this.options.read_only) return false\n\n const { e, node } = options\n const width = this.width || node.size[0]\n const x = e.canvasX - node.pos[0]\n\n // Calculate new value based on drag position\n const slideFactor = clamp((x - 15) / (width - 30), 0, 1)\n const newValue =\n this.options.min + (this.options.max - this.options.min) * slideFactor\n\n if (newValue !== this.value) {\n this.setValue(newValue, options)\n }\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { IStringWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\nexport class TextWidget\n extends BaseWidget<IStringWidget>\n implements IStringWidget\n{\n constructor(widget: IStringWidget, node: LGraphNode) {\n super(widget, node)\n this.type ??= 'string'\n this.value = widget.value?.toString() ?? ''\n }\n\n /**\n * Draws the widget\n * @param ctx The canvas context\n * @param options The options for drawing the widget\n */\n override drawWidget(\n ctx: CanvasRenderingContext2D,\n { width, showText = true }: DrawWidgetOptions\n ) {\n // Store original context attributes\n const { fillStyle, strokeStyle, textAlign } = ctx\n\n this.drawWidgetShape(ctx, { width, showText })\n\n if (showText) {\n this.drawTruncatingText({ ctx, width, leftPadding: 0, rightPadding: 0 })\n }\n\n // Restore original context attributes\n Object.assign(ctx, { textAlign, strokeStyle, fillStyle })\n }\n\n override onClick({ e, node, canvas }: WidgetEventOptions) {\n // Show prompt dialog for text input\n canvas.prompt(\n 'Value',\n this.value,\n (v: string) => {\n if (v !== null) {\n this.setValue(v, { e, node, canvas })\n }\n },\n e,\n this.options?.multiline ?? false\n )\n }\n}\n","import type { ITextareaWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for multi-line text input.\n * This widget only has a Vue implementation.\n */\nexport class TextareaWidget\n extends BaseWidget<ITextareaWidget>\n implements ITextareaWidget\n{\n override type = 'textarea' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n this.drawVueOnlyWarning(ctx, options, 'Textarea')\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This widget only has a Vue implementation\n }\n}\n","import type { ITreeSelectWidget } from '../types/widgets'\nimport { BaseWidget } from './BaseWidget'\nimport type { DrawWidgetOptions, WidgetEventOptions } from './BaseWidget'\n\n/**\n * Widget for hierarchical tree selection.\n * This widget only has a Vue implementation.\n */\nexport class TreeSelectWidget\n extends BaseWidget<ITreeSelectWidget>\n implements ITreeSelectWidget\n{\n override type = 'treeselect' as const\n\n drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {\n this.drawVueOnlyWarning(ctx, options, 'TreeSelect')\n }\n\n onClick(_options: WidgetEventOptions): void {\n // This widget only has a Vue implementation\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type {\n IAssetWidget,\n IBaseWidget,\n IComboWidget,\n IWidget,\n TWidgetType\n} from '@/lib/litegraph/src/types/widgets'\nimport { toClass } from '@/lib/litegraph/src/utils/type'\n\nimport { AssetWidget } from './AssetWidget'\nimport { BaseWidget } from './BaseWidget'\nimport { BooleanWidget } from './BooleanWidget'\nimport { BoundingBoxWidget } from './BoundingBoxWidget'\nimport { ButtonWidget } from './ButtonWidget'\nimport { ChartWidget } from './ChartWidget'\nimport { ColorWidget } from './ColorWidget'\nimport { ComboWidget } from './ComboWidget'\nimport { FileUploadWidget } from './FileUploadWidget'\nimport { GalleriaWidget } from './GalleriaWidget'\nimport { ImageCompareWidget } from './ImageCompareWidget'\nimport { ImageCropWidget } from './ImageCropWidget'\nimport { KnobWidget } from './KnobWidget'\nimport { LegacyWidget } from './LegacyWidget'\nimport { MarkdownWidget } from './MarkdownWidget'\nimport { MultiSelectWidget } from './MultiSelectWidget'\nimport { NumberWidget } from './NumberWidget'\nimport { SelectButtonWidget } from './SelectButtonWidget'\nimport { SliderWidget } from './SliderWidget'\nimport { TextWidget } from './TextWidget'\nimport { TextareaWidget } from './TextareaWidget'\nimport { TreeSelectWidget } from './TreeSelectWidget'\n\nexport type WidgetTypeMap = {\n button: ButtonWidget\n toggle: BooleanWidget\n slider: SliderWidget\n knob: KnobWidget\n combo: ComboWidget\n number: NumberWidget\n string: TextWidget\n text: TextWidget\n custom: LegacyWidget\n fileupload: FileUploadWidget\n color: ColorWidget\n markdown: MarkdownWidget\n treeselect: TreeSelectWidget\n multiselect: MultiSelectWidget\n chart: ChartWidget\n galleria: GalleriaWidget\n imagecompare: ImageCompareWidget\n selectbutton: SelectButtonWidget\n textarea: TextareaWidget\n asset: AssetWidget\n imagecrop: ImageCropWidget\n boundingbox: BoundingBoxWidget\n [key: string]: BaseWidget\n}\n\n/**\n * Convert a widget POJO to a proper widget instance.\n * @param widget The POJO to convert.\n * @param node The node the widget belongs to.\n * @param wrapLegacyWidgets Whether to wrap legacy widgets in a `LegacyWidget` instance.\n * @returns A concrete widget instance.\n */\nexport function toConcreteWidget<TWidget extends IWidget | IBaseWidget>(\n widget: TWidget,\n node: LGraphNode,\n wrapLegacyWidgets?: true\n): WidgetTypeMap[TWidget['type']]\nexport function toConcreteWidget<TWidget extends IWidget | IBaseWidget>(\n widget: TWidget,\n node: LGraphNode,\n wrapLegacyWidgets: false\n): WidgetTypeMap[TWidget['type']] | undefined\nexport function toConcreteWidget<TWidget extends IWidget | IBaseWidget>(\n widget: TWidget,\n node: LGraphNode,\n wrapLegacyWidgets = true\n): WidgetTypeMap[TWidget['type']] | undefined {\n if (widget instanceof BaseWidget) return widget\n\n // Assertion: TypeScript has no concept of \"all strings except X\"\n type RemoveBaseWidgetType<T> = T extends { type: TWidgetType } ? T : never\n const narrowedWidget = widget as RemoveBaseWidgetType<TWidget>\n\n switch (narrowedWidget.type) {\n case 'button':\n return toClass(ButtonWidget, narrowedWidget, node)\n case 'toggle':\n return toClass(BooleanWidget, narrowedWidget, node)\n case 'slider':\n return toClass(SliderWidget, narrowedWidget, node)\n case 'knob':\n return toClass(KnobWidget, narrowedWidget, node)\n case 'combo':\n return toClass(ComboWidget, narrowedWidget, node)\n case 'number':\n return toClass(NumberWidget, narrowedWidget, node)\n case 'string':\n return toClass(TextWidget, narrowedWidget, node)\n case 'text':\n return toClass(TextWidget, narrowedWidget, node)\n case 'fileupload':\n return toClass(FileUploadWidget, narrowedWidget, node)\n case 'color':\n return toClass(ColorWidget, narrowedWidget, node)\n case 'markdown':\n return toClass(MarkdownWidget, narrowedWidget, node)\n case 'treeselect':\n return toClass(TreeSelectWidget, narrowedWidget, node)\n case 'multiselect':\n return toClass(MultiSelectWidget, narrowedWidget, node)\n case 'chart':\n return toClass(ChartWidget, narrowedWidget, node)\n case 'galleria':\n return toClass(GalleriaWidget, narrowedWidget, node)\n case 'imagecompare':\n return toClass(ImageCompareWidget, narrowedWidget, node)\n case 'selectbutton':\n return toClass(SelectButtonWidget, narrowedWidget, node)\n case 'textarea':\n return toClass(TextareaWidget, narrowedWidget, node)\n case 'asset':\n return toClass(AssetWidget, narrowedWidget, node)\n case 'imagecrop':\n return toClass(ImageCropWidget, narrowedWidget, node)\n case 'boundingbox':\n return toClass(BoundingBoxWidget, narrowedWidget, node)\n default: {\n if (wrapLegacyWidgets) return toClass(LegacyWidget, widget, node)\n }\n }\n}\n\n// #region Type Guards\n\n/** Type guard: Narrow **from {@link IBaseWidget}** to {@link IComboWidget}. */\nexport function isComboWidget(widget: IBaseWidget): widget is IComboWidget {\n return widget.type === 'combo'\n}\n\n/** Type guard: Narrow **from {@link IBaseWidget}** to {@link IAssetWidget}. */\nexport function isAssetWidget(widget: IBaseWidget): widget is IAssetWidget {\n return widget.type === 'asset'\n}\n\n// #endregion Type Guards\n","import { LGraphNodeProperties } from '@/lib/litegraph/src/LGraphNodeProperties'\nimport {\n calculateInputSlotPosFromSlot,\n getSlotPosition\n} from '@/renderer/core/canvas/litegraph/slotCalculations'\nimport type { SlotPositionContext } from '@/renderer/core/canvas/litegraph/slotCalculations'\nimport { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'\nimport { LayoutSource } from '@/renderer/core/layout/types'\nimport { adjustColor } from '@/utils/colorUtil'\nimport type { ColorAdjustOptions } from '@/utils/colorUtil'\nimport { commonType, toClass } from '@/lib/litegraph/src/utils/type'\n\nimport { SUBGRAPH_OUTPUT_ID } from '@/lib/litegraph/src/constants'\nimport type { DragAndScale } from './DragAndScale'\nimport type { LGraph } from './LGraph'\nimport { BadgePosition, LGraphBadge } from './LGraphBadge'\nimport { LGraphButton } from './LGraphButton'\nimport type { LGraphButtonOptions } from './LGraphButton'\nimport { LGraphCanvas } from './LGraphCanvas'\nimport { LLink } from './LLink'\nimport type { Reroute, RerouteId } from './Reroute'\nimport { getNodeInputOnPos, getNodeOutputOnPos } from './canvas/measureSlots'\nimport type { IDrawBoundingOptions } from './draw'\nimport { NullGraphError } from './infrastructure/NullGraphError'\nimport type { ReadOnlyRectangle } from './infrastructure/Rectangle'\nimport { Rectangle } from './infrastructure/Rectangle'\nimport type {\n ColorOption,\n CompassCorners,\n DefaultConnectionColors,\n Dictionary,\n IColorable,\n IContextMenuValue,\n IFoundSlot,\n INodeFlags,\n INodeInputSlot,\n INodeOutputSlot,\n INodeSlot,\n INodeSlotContextItem,\n IPinnable,\n ISlotType,\n Panel,\n Point,\n Positionable,\n ReadOnlyRect,\n Rect,\n Size\n} from './interfaces'\nimport { LiteGraph, Subgraph } from './litegraph'\nimport type { LGraphNodeConstructor, SubgraphNode } from './litegraph'\nimport {\n createBounds,\n isInRect,\n isInRectangle,\n isPointInRect,\n snapPoint\n} from './measure'\nimport { NodeInputSlot } from './node/NodeInputSlot'\nimport { NodeOutputSlot } from './node/NodeOutputSlot'\nimport {\n inputAsSerialisable,\n isINodeInputSlot,\n isWidgetInputSlot,\n outputAsSerialisable\n} from './node/slotUtils'\nimport type { SubgraphInputNode } from './subgraph/SubgraphInputNode'\nimport type { SubgraphOutputNode } from './subgraph/SubgraphOutputNode'\nimport type { NodeLike } from './types/NodeLike'\nimport type { CanvasPointerEvent } from './types/events'\nimport {\n LGraphEventMode,\n NodeSlotType,\n RenderShape,\n TitleMode\n} from './types/globalEnums'\nimport type { ISerialisedNode, SubgraphIO } from './types/serialisation'\nimport type {\n IBaseWidget,\n IWidgetOptions,\n TWidgetType,\n TWidgetValue\n} from './types/widgets'\nimport { findFreeSlotOfType } from './utils/collections'\nimport { warnDeprecated } from './utils/feedback'\nimport { distributeSpace } from './utils/spaceDistribution'\nimport { truncateText } from './utils/textUtils'\nimport { BaseWidget } from './widgets/BaseWidget'\nimport { toConcreteWidget } from './widgets/widgetMap'\nimport type { WidgetTypeMap } from './widgets/widgetMap'\n\n// #region Types\n\nexport type NodeId = number | string\n\nexport type NodeProperty = string | number | boolean | object\n\ninterface INodePropertyInfo {\n name?: string\n type?: string\n default_value?: NodeProperty\n widget?: string\n label?: string\n values?: TWidgetValue[]\n}\n\ninterface IMouseOverData {\n inputId?: number\n outputId?: number\n overWidget?: IBaseWidget\n}\n\ninterface ConnectByTypeOptions {\n /** @deprecated Events */\n createEventInCase?: boolean\n /** Allow our wildcard slot to connect to typed slots on remote node. Default: true */\n wildcardToTyped?: boolean\n /** Allow our typed slot to connect to wildcard slots on remote node. Default: true */\n typedToWildcard?: boolean\n /** The {@link Reroute.id} that the connection is being dragged from. */\n afterRerouteId?: RerouteId\n}\n\n/** Internal type used for type safety when implementing generic checks for inputs & outputs */\ninterface IGenericLinkOrLinks {\n links?: INodeOutputSlot['links']\n link?: INodeInputSlot['link']\n}\n\ninterface FindFreeSlotOptions {\n /** Slots matching these types will be ignored. Default: [] */\n typesNotAccepted?: ISlotType[]\n /** If true, the slot itself is returned instead of the index. Default: false */\n returnObj?: boolean\n}\n\ninterface DrawSlotsOptions {\n fromSlot?: INodeInputSlot | INodeOutputSlot\n colorContext: DefaultConnectionColors\n editorAlpha: number\n lowQuality: boolean\n}\n\ninterface DrawWidgetsOptions {\n lowQuality?: boolean\n editorAlpha?: number\n}\n\ninterface DrawTitleOptions {\n scale: number\n title_height?: number\n low_quality?: boolean\n}\n\ninterface DrawTitleTextOptions extends DrawTitleOptions {\n default_title_color: string\n}\n\nexport interface DrawTitleBoxOptions extends DrawTitleOptions {\n box_size?: number\n}\n\n/*\ntitle: string\npos: [x,y]\nsize: [x,y]\n\ninput|output: every connection\n + { name:string, type:string, pos: [x,y]=Optional, direction: \"input\"|\"output\", links: Array });\n\ngeneral properties:\n + clip_area: if you render outside the node, it will be clipped\n + unsafe_execution: not allowed for safe execution\n + skip_repeated_outputs: when adding new outputs, it won't show if there is one already connected\n + resizable: if set to false it won't be resizable with the mouse\n + widgets_start_y: widgets start at y distance from the top of the node\n\nflags object:\n + collapsed: if it is collapsed\n\nsupported callbacks:\n + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading)\n + onRemoved: when removed from graph\n + onStart: when the graph starts playing\n + onStop: when the graph stops playing\n + onDrawForeground: render the inside widgets inside the node\n + onDrawBackground: render the background area inside the node (only in edit mode)\n + onMouseDown\n + onMouseMove\n + onMouseUp\n + onMouseEnter\n + onMouseLeave\n + onExecute: execute the node\n + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour)\n + onGetInputs: returns an array of possible inputs\n + onGetOutputs: returns an array of possible outputs\n + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h])\n + onDblClick: double clicked in the node\n + onNodeTitleDblClick: double clicked in the node title\n + onInputDblClick: input slot double clicked (can be used to automatically create a node connected)\n + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected)\n + onConfigure: called after the node has been configured\n + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data)\n + onSelected\n + onDeselected\n + onDropItem : DOM item dropped over the node\n + onDropFile : file dropped over the node\n + onConnectInput : if returns false the incoming connection will be canceled\n + onConnectionsChange : a connection changed (new one or removed) (NodeSlotType.INPUT or NodeSlotType.OUTPUT, slot, true if connected, link_info, input_info )\n + onAction: action slot triggered\n + getExtraMenuOptions: to add option to context menu\n*/\n\nexport interface LGraphNode {\n constructor: LGraphNodeConstructor\n}\n\n// #endregion Types\n\n/**\n * Base class for all nodes\n * @param title a name for the node\n * @param type a type for the node\n */\n\nexport class LGraphNode\n implements NodeLike, Positionable, IPinnable, IColorable\n{\n // Static properties used by dynamic child classes\n static title?: string\n static MAX_CONSOLE?: number\n static type?: string\n static category?: string\n static description?: string\n static filter?: string\n static skip_list?: boolean\n\n static resizeHandleSize = 15\n static resizeEdgeSize = 5\n\n /** Default setting for {@link LGraphNode.connectInputToOutput}. @see {@link INodeFlags.keepAllLinksOnBypass} */\n static keepAllLinksOnBypass: boolean = false\n\n /** The title text of the node. */\n title: string\n /**\n * The font style used to render the node's title text.\n */\n get titleFontStyle(): string {\n return `${LiteGraph.NODE_TEXT_SIZE}px ${LiteGraph.NODE_FONT}`\n }\n\n get innerFontStyle(): string {\n return `normal ${LiteGraph.NODE_SUBTEXT_SIZE}px ${LiteGraph.NODE_FONT}`\n }\n\n get displayType(): string {\n return this.type\n }\n\n graph: LGraph | Subgraph | null = null\n id: NodeId\n type: string = ''\n inputs: INodeInputSlot[] = []\n outputs: INodeOutputSlot[] = []\n\n #concreteInputs: NodeInputSlot[] = []\n #concreteOutputs: NodeOutputSlot[] = []\n\n properties: Dictionary<NodeProperty | undefined> = {}\n properties_info: INodePropertyInfo[] = []\n flags: INodeFlags = {}\n widgets?: IBaseWidget[]\n\n /** Property manager for this node */\n changeTracker: LGraphNodeProperties\n\n /**\n * The amount of space available for widgets to grow into.\n * @see {@link layoutWidgets}\n */\n freeWidgetSpace?: number\n\n locked?: boolean\n\n /** Execution order, automatically computed during run @see {@link LGraph.computeExecutionOrder} */\n order: number = 0\n mode: LGraphEventMode = LGraphEventMode.ALWAYS\n last_serialization?: ISerialisedNode\n serialize_widgets?: boolean\n /**\n * The overridden fg color used to render the node.\n * @see {@link renderingColor}\n */\n color?: string\n /**\n * The overridden bg color used to render the node.\n * @see {@link renderingBgColor}\n */\n bgcolor?: string\n /**\n * The overridden box color used to render the node.\n * @see {@link renderingBoxColor}\n */\n boxcolor?: string\n\n /** The fg color used to render the node. */\n get renderingColor(): string {\n const baseColor =\n this.color || this.constructor.color || LiteGraph.NODE_DEFAULT_COLOR\n return adjustColor(baseColor, { lightness: LiteGraph.nodeLightness })\n }\n\n /** The bg color used to render the node. */\n get renderingBgColor(): string {\n const baseBgColor =\n this.bgcolor || this.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR\n const adjustments: ColorAdjustOptions = {\n opacity: LiteGraph.nodeOpacity,\n lightness: LiteGraph.nodeLightness\n }\n\n return adjustColor(\n this.mode === LGraphEventMode.BYPASS\n ? LiteGraph.NODE_DEFAULT_BYPASS_COLOR\n : baseBgColor,\n adjustments\n )\n }\n\n /** The box color used to render the node. */\n get renderingBoxColor(): string {\n if (this.boxcolor) return this.boxcolor\n\n if (LiteGraph.node_box_coloured_when_on) {\n if (this.action_triggered) return '#FFF'\n if (this.execute_triggered) return '#AAA'\n }\n\n if (LiteGraph.node_box_coloured_by_mode) {\n const modeColour =\n LiteGraph.NODE_MODES_COLORS[this.mode ?? LGraphEventMode.ALWAYS]\n if (modeColour) return modeColour\n }\n return LiteGraph.NODE_DEFAULT_BOXCOLOR\n }\n\n /** @inheritdoc {@link IColorable.setColorOption} */\n setColorOption(colorOption: ColorOption | null): void {\n if (colorOption == null) {\n this.color = undefined\n this.bgcolor = undefined\n } else {\n this.color = colorOption.color\n this.bgcolor = colorOption.bgcolor\n }\n }\n\n /** @inheritdoc {@link IColorable.getColorOption} */\n getColorOption(): ColorOption | null {\n return (\n Object.values(LGraphCanvas.node_colors).find(\n (colorOption) =>\n colorOption.color === this.color &&\n colorOption.bgcolor === this.bgcolor\n ) ?? null\n )\n }\n\n /**\n * The stroke styles that should be applied to the node.\n */\n strokeStyles: Record<\n string,\n (this: LGraphNode) => IDrawBoundingOptions | undefined\n >\n\n /**\n * The progress of node execution. Used to render a progress bar. Value between 0 and 1.\n */\n progress?: number\n\n exec_version?: number\n action_call?: string\n execute_triggered?: number\n action_triggered?: number\n widgets_up?: boolean\n widgets_start_y?: number\n lostFocusAt?: number\n gotFocusAt?: number\n badges: (LGraphBadge | (() => LGraphBadge))[] = []\n title_buttons: LGraphButton[] = []\n badgePosition: BadgePosition = BadgePosition.TopLeft\n onOutputRemoved?(this: LGraphNode, slot: number): void\n onInputRemoved?(this: LGraphNode, slot: number, input: INodeInputSlot): void\n /**\n * The width of the node when collapsed.\n * Updated by {@link LGraphCanvas.drawNode}\n */\n _collapsed_width?: number\n /**\n * Called once at the start of every frame. Caller may change the values in {@link out}, which will be reflected in {@link boundingRect}.\n * WARNING: Making changes to boundingRect via onBounding is poorly supported, and will likely result in strange behaviour.\n */\n onBounding?(this: LGraphNode, out: Rect): void\n console?: string[]\n _level?: number\n _shape?: RenderShape\n mouseOver?: IMouseOverData\n redraw_on_mouse?: boolean\n resizable?: boolean\n clonable?: boolean\n _relative_id?: number\n clip_area?: boolean\n ignore_remove?: boolean\n has_errors?: boolean\n removable?: boolean\n block_delete?: boolean\n selected?: boolean\n showAdvanced?: boolean\n\n declare comfyDynamic?: Record<string, object>\n declare comfyClass?: string\n declare isVirtualNode?: boolean\n applyToGraph?(extraLinks?: LLink[]): void\n\n isSubgraphNode(): this is SubgraphNode {\n return false\n }\n\n /** @inheritdoc {@link renderArea} */\n #renderArea = new Rectangle()\n /**\n * Rect describing the node area, including shadows and any protrusions.\n * Determines if the node is visible. Calculated once at the start of every frame.\n */\n get renderArea(): ReadOnlyRect {\n return this.#renderArea\n }\n\n /** @inheritdoc {@link boundingRect} */\n #boundingRect: Rectangle = new Rectangle()\n /**\n * Cached node position & area as `x, y, width, height`. Includes changes made by {@link onBounding}, if present.\n *\n * Determines the node hitbox and other rendering effects. Calculated once at the start of every frame.\n */\n get boundingRect(): ReadOnlyRectangle {\n return this.#boundingRect\n }\n\n /** The offset from {@link pos} to the top-left of {@link boundingRect}. */\n get boundingOffset(): Readonly<Point> {\n const {\n pos: [posX, posY],\n boundingRect: [bX, bY]\n } = this\n return [posX - bX, posY - bY]\n }\n\n /** {@link pos} and {@link size} values are backed by this {@link Rectangle}. */\n _posSize = new Rectangle()\n _pos: Point = this._posSize.pos\n _size: Size = this._posSize.size\n\n public get pos() {\n return this._pos\n }\n\n /** Node position does not necessarily correlate to the top-left corner. */\n public set pos(value) {\n if (!value || value.length < 2) return\n\n this._pos[0] = value[0]\n this._pos[1] = value[1]\n }\n\n public get size() {\n return this._size\n }\n\n public set size(value) {\n if (!value || value.length < 2) return\n\n this._size[0] = value[0]\n this._size[1] = value[1]\n }\n\n /**\n * The size of the node used for rendering.\n */\n get renderingSize(): Size {\n return this.flags.collapsed ? [this._collapsed_width ?? 0, 0] : this._size\n }\n\n get shape(): RenderShape | undefined {\n return this._shape\n }\n\n set shape(v: RenderShape | 'default' | 'box' | 'round' | 'circle' | 'card') {\n const oldValue = this._shape\n switch (v) {\n case 'default':\n this._shape = undefined\n break\n case 'box':\n this._shape = RenderShape.BOX\n break\n case 'round':\n this._shape = RenderShape.ROUND\n break\n case 'circle':\n this._shape = RenderShape.CIRCLE\n break\n case 'card':\n this._shape = RenderShape.CARD\n break\n default:\n this._shape = v\n }\n if (oldValue !== this._shape) {\n this.graph?.trigger('node:property:changed', {\n nodeId: this.id,\n property: 'shape',\n oldValue,\n newValue: this._shape\n })\n }\n }\n\n /**\n * The shape of the node used for rendering. @see {@link RenderShape}\n */\n get renderingShape(): RenderShape {\n return this._shape || this.constructor.shape || LiteGraph.NODE_DEFAULT_SHAPE\n }\n\n public get is_selected(): boolean | undefined {\n return this.selected\n }\n\n public set is_selected(value: boolean) {\n this.selected = value\n }\n\n public get title_mode(): TitleMode {\n return this.constructor.title_mode ?? TitleMode.NORMAL_TITLE\n }\n\n onConnectInput?(\n this: LGraphNode,\n target_slot: number,\n type: unknown,\n output: INodeOutputSlot | SubgraphIO,\n node: LGraphNode | SubgraphInputNode,\n slot: number\n ): boolean\n onConnectOutput?(\n this: LGraphNode,\n slot: number,\n type: unknown,\n input: INodeInputSlot | SubgraphIO,\n target_node: number | LGraphNode | SubgraphOutputNode,\n target_slot: number\n ): boolean\n onResize?(this: LGraphNode, size: Size): void\n onPropertyChanged?(\n this: LGraphNode,\n name: string,\n value: unknown,\n prev_value?: unknown\n ): boolean\n /** Called for each connection that is created, updated, or removed. This includes \"restored\" connections when deserialising. */\n onConnectionsChange?(\n this: LGraphNode,\n type: ISlotType,\n index: number,\n isConnected: boolean,\n link_info: LLink | null | undefined,\n inputOrOutput: INodeInputSlot | INodeOutputSlot | SubgraphIO\n ): void\n onInputAdded?(this: LGraphNode, input: INodeInputSlot): void\n onOutputAdded?(this: LGraphNode, output: INodeOutputSlot): void\n onConfigure?(this: LGraphNode, serialisedNode: ISerialisedNode): void\n onSerialize?(this: LGraphNode, serialised: ISerialisedNode): void\n onExecute?(\n this: LGraphNode,\n param?: unknown,\n options?: { action_call?: string }\n ): void\n onAction?(\n this: LGraphNode,\n action: string,\n param: unknown,\n options: { action_call?: string }\n ): void\n onDrawBackground?(this: LGraphNode, ctx: CanvasRenderingContext2D): void\n onNodeCreated?(this: LGraphNode): void\n /**\n * Callback invoked by {@link connect} to override the target slot index.\n * Its return value overrides the target index selection.\n * @param target_slot The current input slot index\n * @param requested_slot The originally requested slot index - could be negative, or if using (deprecated) name search, a string\n * @returns {number | null} If a number is returned, the connection will be made to that input index.\n * If an invalid index or non-number (false, null, NaN etc) is returned, the connection will be cancelled.\n */\n onBeforeConnectInput?(\n this: LGraphNode,\n target_slot: number,\n requested_slot?: number | string\n ): number | false | null\n onShowCustomPanelInfo?(this: LGraphNode, panel: Panel): void\n onAddPropertyToPanel?(this: LGraphNode, pName: string, panel: Panel): boolean\n onWidgetChanged?(\n this: LGraphNode,\n name: string,\n value: unknown,\n old_value: unknown,\n w: IBaseWidget\n ): void\n onDeselected?(this: LGraphNode): void\n onKeyUp?(this: LGraphNode, e: KeyboardEvent): void\n onKeyDown?(this: LGraphNode, e: KeyboardEvent): void\n onSelected?(this: LGraphNode): void\n getExtraMenuOptions?(\n this: LGraphNode,\n canvas: LGraphCanvas,\n options: (IContextMenuValue<unknown> | null)[]\n ): (IContextMenuValue<unknown> | null)[]\n getMenuOptions?(this: LGraphNode, canvas: LGraphCanvas): IContextMenuValue[]\n onAdded?(this: LGraphNode, graph: LGraph): void\n onDrawCollapsed?(\n this: LGraphNode,\n ctx: CanvasRenderingContext2D,\n cavnas: LGraphCanvas\n ): boolean\n onDrawForeground?(\n this: LGraphNode,\n ctx: CanvasRenderingContext2D,\n canvas: LGraphCanvas,\n canvasElement: HTMLCanvasElement\n ): void\n onMouseLeave?(this: LGraphNode, e: CanvasPointerEvent): void\n /**\n * Override the default slot menu options.\n */\n getSlotMenuOptions?(this: LGraphNode, slot: IFoundSlot): IContextMenuValue[]\n /**\n * Add extra menu options to the slot context menu.\n */\n getExtraSlotMenuOptions?(\n this: LGraphNode,\n slot: IFoundSlot\n ): IContextMenuValue[]\n\n // FIXME: Re-typing\n onDropItem?(this: LGraphNode, event: Event): boolean\n onDropData?(\n this: LGraphNode,\n data: string | ArrayBuffer,\n filename: string,\n file: File\n ): void\n onDropFile?(this: LGraphNode, file: File): void\n onInputClick?(this: LGraphNode, index: number, e: CanvasPointerEvent): void\n onInputDblClick?(this: LGraphNode, index: number, e: CanvasPointerEvent): void\n onOutputClick?(this: LGraphNode, index: number, e: CanvasPointerEvent): void\n onOutputDblClick?(\n this: LGraphNode,\n index: number,\n e: CanvasPointerEvent\n ): void\n onGetPropertyInfo?(this: LGraphNode, property: string): INodePropertyInfo\n onNodeOutputAdd?(this: LGraphNode, value: unknown): void\n onNodeInputAdd?(this: LGraphNode, value: unknown): void\n onMenuNodeInputs?(\n this: LGraphNode,\n entries: (IContextMenuValue<INodeSlotContextItem> | null)[]\n ): (IContextMenuValue<INodeSlotContextItem> | null)[]\n onMenuNodeOutputs?(\n this: LGraphNode,\n entries: (IContextMenuValue<INodeSlotContextItem> | null)[]\n ): (IContextMenuValue<INodeSlotContextItem> | null)[]\n onMouseUp?(\n this: LGraphNode,\n e: CanvasPointerEvent,\n pos: Point,\n canvas: LGraphCanvas\n ): void\n onMouseEnter?(this: LGraphNode, e: CanvasPointerEvent): void\n /** Blocks drag if return value is truthy. @param pos Offset from {@link LGraphNode.pos}. */\n onMouseDown?(\n this: LGraphNode,\n e: CanvasPointerEvent,\n pos: Point,\n canvas: LGraphCanvas\n ): boolean\n /** @param pos Offset from {@link LGraphNode.pos}. */\n onDblClick?(\n this: LGraphNode,\n e: CanvasPointerEvent,\n pos: Point,\n canvas: LGraphCanvas\n ): void\n /** @param pos Offset from {@link LGraphNode.pos}. */\n onNodeTitleDblClick?(\n this: LGraphNode,\n e: CanvasPointerEvent,\n pos: Point,\n canvas: LGraphCanvas\n ): void\n onDrawTitle?(this: LGraphNode, ctx: CanvasRenderingContext2D): void\n onDrawTitleText?(\n this: LGraphNode,\n ctx: CanvasRenderingContext2D,\n title_height: number,\n size: Size,\n scale: number,\n title_text_font: string,\n selected?: boolean\n ): void\n onDrawTitleBox?(\n this: LGraphNode,\n ctx: CanvasRenderingContext2D,\n title_height: number,\n size: Size,\n scale: number\n ): void\n onDrawTitleBar?(\n this: LGraphNode,\n ctx: CanvasRenderingContext2D,\n title_height: number,\n size: Size,\n scale: number,\n fgcolor: string\n ): void\n onRemoved?(this: LGraphNode): void\n onMouseMove?(\n this: LGraphNode,\n e: CanvasPointerEvent,\n pos: Point,\n arg2: LGraphCanvas\n ): void\n onPropertyChange?(this: LGraphNode): void\n updateOutputData?(this: LGraphNode, origin_slot: number): void\n\n #getErrorStrokeStyle(this: LGraphNode): IDrawBoundingOptions | undefined {\n if (this.has_errors) {\n return {\n padding: 12,\n lineWidth: 10,\n color: LiteGraph.NODE_ERROR_COLOUR\n }\n }\n }\n\n #getSelectedStrokeStyle(this: LGraphNode): IDrawBoundingOptions | undefined {\n if (this.selected) {\n return {\n padding: this.has_errors ? 20 : undefined\n }\n }\n }\n\n constructor(title: string, type?: string) {\n this.id = LiteGraph.use_uuids ? LiteGraph.uuidv4() : -1\n this.title = title || 'Unnamed'\n this.type = type ?? ''\n this.size = [LiteGraph.NODE_WIDTH, 60]\n this.pos = [10, 10]\n this.strokeStyles = {\n error: this.#getErrorStrokeStyle,\n selected: this.#getSelectedStrokeStyle\n }\n // Initialize property manager with tracked properties\n this.changeTracker = new LGraphNodeProperties(this)\n }\n\n /** Internal callback for subgraph nodes. Do not implement externally. */\n _internalConfigureAfterSlots?(): void\n\n /**\n * configure a node from an object containing the serialized info\n */\n configure(info: ISerialisedNode): void {\n if (this.graph) {\n this.graph._version++\n }\n for (const j in info) {\n if (j == 'properties') {\n // i don't want to clone properties, I want to reuse the old container\n for (const k in info.properties) {\n this.properties[k] = info.properties[k]\n this.onPropertyChanged?.(k, info.properties[k])\n }\n continue\n }\n\n // @ts-expect-error #594\n if (info[j] == null) {\n continue\n // @ts-expect-error #594\n } else if (typeof info[j] == 'object') {\n // @ts-expect-error #594\n if (this[j]?.configure) {\n // @ts-expect-error #594\n this[j]?.configure(info[j])\n } else {\n // @ts-expect-error #594\n this[j] = LiteGraph.cloneObject(info[j], this[j])\n }\n } else {\n // value\n // @ts-expect-error #594\n this[j] = info[j]\n }\n }\n\n if (!info.title) {\n this.title = this.constructor.title\n }\n\n this.inputs ??= []\n this.inputs = this.inputs.map((input) =>\n toClass(NodeInputSlot, input, this)\n )\n for (const [i, input] of this.inputs.entries()) {\n const link =\n this.graph && input.link != null\n ? this.graph._links.get(input.link)\n : null\n this.onConnectionsChange?.(NodeSlotType.INPUT, i, true, link, input)\n this.onInputAdded?.(input)\n }\n\n this.outputs ??= []\n this.outputs = this.outputs.map((output) =>\n toClass(NodeOutputSlot, output, this)\n )\n for (const [i, output] of this.outputs.entries()) {\n if (!output.links) continue\n\n for (const linkId of output.links) {\n const link = this.graph ? this.graph._links.get(linkId) : null\n this.onConnectionsChange?.(NodeSlotType.OUTPUT, i, true, link, output)\n }\n this.onOutputAdded?.(output)\n }\n\n // SubgraphNode callback.\n this._internalConfigureAfterSlots?.()\n\n if (this.widgets) {\n for (const w of this.widgets) {\n if (!w) continue\n\n const input = this.inputs.find((i) => i.widget?.name === w.name)\n if (input?.label) w.label = input.label\n\n if (\n w.options?.property &&\n this.properties[w.options.property] != undefined\n )\n w.value = JSON.parse(\n JSON.stringify(this.properties[w.options.property])\n )\n }\n\n if (info.widgets_values) {\n let i = 0\n for (const widget of this.widgets ?? []) {\n if (widget.serialize === false) continue\n if (i >= info.widgets_values.length) break\n widget.value = info.widgets_values[i++]\n }\n }\n }\n\n // Sync the state of this.resizable.\n if (this.pinned) this.resizable = false\n\n this.onConfigure?.(info)\n }\n\n /**\n * serialize the content\n */\n serialize(): ISerialisedNode {\n // create serialization object\n const o: ISerialisedNode = {\n id: this.id,\n type: this.type,\n pos: [this.pos[0], this.pos[1]],\n size: [this.size[0], this.size[1]],\n flags: LiteGraph.cloneObject(this.flags),\n order: this.order,\n mode: this.mode,\n showAdvanced: this.showAdvanced\n }\n\n // special case for when there were errors\n if (this.constructor === LGraphNode && this.last_serialization)\n return { ...this.last_serialization, mode: o.mode, pos: o.pos }\n\n if (this.inputs)\n o.inputs = this.inputs.map((input) => inputAsSerialisable(input))\n if (this.outputs)\n // @ts-expect-error - Output serialization type mismatch\n o.outputs = this.outputs.map((output) => outputAsSerialisable(output))\n\n if (this.title && this.title != this.constructor.title) o.title = this.title\n\n if (this.properties) o.properties = LiteGraph.cloneObject(this.properties)\n\n const { widgets } = this\n if (widgets && this.serialize_widgets) {\n o.widgets_values = []\n for (const [i, widget] of widgets.entries()) {\n if (widget.serialize === false) continue\n // @ts-expect-error #595 No-null\n o.widgets_values[i] = widget ? widget.value : null\n }\n }\n\n if (!o.type && this.constructor.type) o.type = this.constructor.type\n\n if (this.color) o.color = this.color\n if (this.bgcolor) o.bgcolor = this.bgcolor\n if (this.boxcolor) o.boxcolor = this.boxcolor\n if (this.shape) o.shape = this.shape\n\n if (this.onSerialize?.(o))\n console.warn(\n \"node onSerialize shouldn't return anything, data should be stored in the object pass in the first parameter\"\n )\n\n return o\n }\n\n /* Creates a clone of this node */\n clone(): LGraphNode | null {\n if (this.type == null) return null\n const node = LiteGraph.createNode(this.type)\n if (!node) return null\n\n // we clone it because serialize returns shared containers\n const data = LiteGraph.cloneObject(this.serialize())\n const { inputs, outputs } = data\n\n // remove links\n if (inputs) {\n for (const input of inputs) {\n input.link = null\n }\n }\n\n if (outputs) {\n for (const { links } of outputs) {\n if (links) links.length = 0\n }\n }\n\n // @ts-expect-error Exceptional case: id is removed so that the graph can assign a new one on add.\n data.id = undefined\n\n if (LiteGraph.use_uuids) data.id = LiteGraph.uuidv4()\n\n node.configure(data)\n\n return node\n }\n\n /**\n * serialize and stringify\n */\n toString(): string {\n return JSON.stringify(this.serialize())\n }\n\n /**\n * get the title string\n */\n getTitle(): string {\n return this.title || this.constructor.title\n }\n\n /**\n * sets the value of a property\n * @param name\n * @param value\n */\n setProperty(name: string, value: TWidgetValue): void {\n this.properties ||= {}\n if (value === this.properties[name]) return\n\n const prev_value = this.properties[name]\n this.properties[name] = value\n // abort change\n if (this.onPropertyChanged?.(name, value, prev_value) === false)\n this.properties[name] = prev_value\n\n if (this.widgets) {\n for (const w of this.widgets) {\n if (!w) continue\n\n if (w.options.property == name) {\n w.value = value\n break\n }\n }\n }\n }\n\n /**\n * sets the output data\n * @param slot\n * @param data\n */\n setOutputData(\n slot: number,\n data: number | string | boolean | { toToolTip?(): string }\n ): void {\n const { outputs } = this\n if (!outputs) return\n\n // this maybe slow and a niche case\n if (slot == -1 || slot >= outputs.length) return\n\n const output_info = outputs[slot]\n if (!output_info) return\n\n // store data in the output itself in case we want to debug\n output_info._data = data\n\n if (!this.graph) throw new NullGraphError()\n\n // if there are connections, pass the data to the connections\n const { links } = outputs[slot]\n if (links) {\n for (const id of links) {\n const link = this.graph._links.get(id)\n if (link) link.data = data\n }\n }\n }\n\n /**\n * sets the output data type, useful when you want to be able to overwrite the data type\n */\n setOutputDataType(slot: number, type: ISlotType): void {\n const { outputs } = this\n if (!outputs || slot == -1 || slot >= outputs.length) return\n\n const output_info = outputs[slot]\n if (!output_info) return\n // store data in the output itself in case we want to debug\n output_info.type = type\n\n if (!this.graph) throw new NullGraphError()\n\n // if there are connections, pass the data to the connections\n const { links } = outputs[slot]\n if (links) {\n for (const id of links) {\n const link = this.graph._links.get(id)\n if (link) link.type = type\n }\n }\n }\n\n /**\n * Retrieves the input data (data traveling through the connection) from one slot\n * @param slot\n * @param force_update if set to true it will force the connected node of this slot to output data into this link\n * @returns data or if it is not connected returns undefined\n */\n getInputData(slot: number, force_update?: boolean): unknown {\n if (!this.inputs) return\n\n if (slot >= this.inputs.length || this.inputs[slot].link == null) return\n if (!this.graph) throw new NullGraphError()\n\n const link_id = this.inputs[slot].link\n const link = this.graph._links.get(link_id)\n // bug: weird case but it happens sometimes\n if (!link) return null\n\n if (!force_update) return link.data\n\n // special case: used to extract data from the incoming connection before the graph has been executed\n const node = this.graph.getNodeById(link.origin_id)\n if (!node) return link.data\n\n if (node.updateOutputData) {\n node.updateOutputData(link.origin_slot)\n } else {\n node.onExecute?.()\n }\n\n return link.data\n }\n\n /**\n * Retrieves the input data type (in case this supports multiple input types)\n * @param slot\n * @returns datatype in string format\n */\n getInputDataType(slot: number): ISlotType | null {\n if (!this.inputs) return null\n if (slot >= this.inputs.length || this.inputs[slot].link == null)\n return null\n if (!this.graph) throw new NullGraphError()\n\n const link_id = this.inputs[slot].link\n const link = this.graph._links.get(link_id)\n // bug: weird case but it happens sometimes\n if (!link) return null\n\n const node = this.graph.getNodeById(link.origin_id)\n if (!node) return link.type\n\n const output_info = node.outputs[link.origin_slot]\n return output_info ? output_info.type : null\n }\n\n /**\n * Retrieves the input data from one slot using its name instead of slot number\n * @param slot_name\n * @param force_update if set to true it will force the connected node of this slot to output data into this link\n * @returns data or if it is not connected returns null\n */\n getInputDataByName(slot_name: string, force_update: boolean): unknown {\n const slot = this.findInputSlot(slot_name)\n return slot == -1 ? null : this.getInputData(slot, force_update)\n }\n\n /**\n * tells you if there is a connection in one input slot\n * @param slot The 0-based index of the input to check\n * @returns `true` if the input slot has a link ID (does not perform validation)\n */\n isInputConnected(slot: number): boolean {\n if (!this.inputs) return false\n return slot < this.inputs.length && this.inputs[slot].link != null\n }\n\n /**\n * tells you info about an input connection (which node, type, etc)\n * @returns object or null { link: id, name: string, type: string or 0 }\n */\n getInputInfo(slot: number): INodeInputSlot | null {\n return !this.inputs || !(slot < this.inputs.length)\n ? null\n : this.inputs[slot]\n }\n\n /**\n * Returns the link info in the connection of an input slot\n * @returns object or null\n */\n getInputLink(slot: number): LLink | null {\n if (!this.inputs) return null\n\n if (slot < this.inputs.length) {\n if (!this.graph) throw new NullGraphError()\n\n const input = this.inputs[slot]\n if (input.link != null) {\n return this.graph._links.get(input.link) ?? null\n }\n }\n return null\n }\n\n /**\n * returns the node connected in the input slot\n * @returns node or null\n */\n getInputNode(slot: number): LGraphNode | null {\n if (!this.inputs) return null\n if (slot >= this.inputs.length) return null\n\n const input = this.inputs[slot]\n if (!input || input.link === null) return null\n if (!this.graph) throw new NullGraphError()\n\n const link_info = this.graph._links.get(input.link)\n if (!link_info) return null\n\n return this.graph.getNodeById(link_info.origin_id)\n }\n\n /**\n * returns the value of an input with this name, otherwise checks if there is a property with that name\n * @returns value\n */\n getInputOrProperty(name: string): unknown {\n const { inputs } = this\n if (!inputs?.length) {\n return this.properties ? this.properties[name] : null\n }\n if (!this.graph) throw new NullGraphError()\n\n for (const input of inputs) {\n if (name == input.name && input.link != null) {\n const link = this.graph._links.get(input.link)\n if (link) return link.data\n }\n }\n return this.properties[name]\n }\n\n /**\n * tells you the last output data that went in that slot\n * @returns object or null\n */\n getOutputData(slot: number): unknown {\n if (!this.outputs) return null\n if (slot >= this.outputs.length) return null\n\n const info = this.outputs[slot]\n return info._data\n }\n\n /**\n * tells you info about an output connection (which node, type, etc)\n * @returns object or null { name: string, type: string, links: [ ids of links in number ] }\n */\n getOutputInfo(slot: number): INodeOutputSlot | null {\n return !this.outputs || !(slot < this.outputs.length)\n ? null\n : this.outputs[slot]\n }\n\n /**\n * tells you if there is a connection in one output slot\n */\n isOutputConnected(slot: number): boolean {\n if (!this.outputs) return false\n return (\n slot < this.outputs.length && Number(this.outputs[slot].links?.length) > 0\n )\n }\n\n /**\n * tells you if there is any connection in the output slots\n */\n isAnyOutputConnected(): boolean {\n const { outputs } = this\n if (!outputs) return false\n\n for (const output of outputs) {\n if (output.links?.length) return true\n }\n return false\n }\n\n /**\n * retrieves all the nodes connected to this output slot\n */\n getOutputNodes(slot: number): LGraphNode[] | null {\n const { outputs } = this\n if (!outputs || outputs.length == 0) return null\n\n if (slot >= outputs.length) return null\n\n const { links } = outputs[slot]\n if (!links || links.length == 0) return null\n if (!this.graph) throw new NullGraphError()\n\n const r: LGraphNode[] = []\n for (const id of links) {\n const link = this.graph._links.get(id)\n if (link) {\n const target_node = this.graph.getNodeById(link.target_id)\n if (target_node) {\n r.push(target_node)\n }\n }\n }\n return r\n }\n\n addOnTriggerInput(): number {\n const trigS = this.findInputSlot('onTrigger')\n if (trigS == -1) {\n this.addInput('onTrigger', LiteGraph.EVENT, {\n nameLocked: true\n })\n return this.findInputSlot('onTrigger')\n }\n return trigS\n }\n\n addOnExecutedOutput(): number {\n const trigS = this.findOutputSlot('onExecuted')\n if (trigS == -1) {\n this.addOutput('onExecuted', LiteGraph.ACTION, {\n nameLocked: true\n })\n return this.findOutputSlot('onExecuted')\n }\n return trigS\n }\n\n onAfterExecuteNode(param: unknown, options?: { action_call?: string }) {\n const trigS = this.findOutputSlot('onExecuted')\n if (trigS != -1) {\n this.triggerSlot(trigS, param, null, options)\n }\n }\n\n changeMode(modeTo: number): boolean {\n switch (modeTo) {\n case LGraphEventMode.ON_EVENT:\n break\n\n case LGraphEventMode.ON_TRIGGER:\n this.addOnTriggerInput()\n this.addOnExecutedOutput()\n break\n\n case LGraphEventMode.NEVER:\n break\n\n case LGraphEventMode.ALWAYS:\n break\n\n // @ts-expect-error Not impl.\n case LiteGraph.ON_REQUEST:\n break\n\n default:\n return false\n break\n }\n this.mode = modeTo\n return true\n }\n\n /**\n * Triggers the node code execution, place a boolean/counter to mark the node as being executed\n */\n doExecute(param?: unknown, options?: { action_call?: string }): void {\n options = options || {}\n if (this.onExecute) {\n // enable this to give the event an ID\n options.action_call ||= `${this.id}_exec_${Math.floor(Math.random() * 9999)}`\n if (!this.graph) throw new NullGraphError()\n\n // @ts-expect-error Technically it works when id is a string. Array gets props.\n this.graph.nodes_executing[this.id] = true\n this.onExecute(param, options)\n // @ts-expect-error deprecated\n this.graph.nodes_executing[this.id] = false\n\n // save execution/action ref\n this.exec_version = this.graph.iteration\n if (options?.action_call) {\n this.action_call = options.action_call\n // @ts-expect-error deprecated\n this.graph.nodes_executedAction[this.id] = options.action_call\n }\n }\n // the nFrames it will be used (-- each step), means \"how old\" is the event\n this.execute_triggered = 2\n this.onAfterExecuteNode?.(param, options)\n }\n\n /**\n * Triggers an action, wrapped by logics to control execution flow\n * @param action name\n */\n actionDo(\n action: string,\n param: unknown,\n options: { action_call?: string }\n ): void {\n options = options || {}\n if (this.onAction) {\n // enable this to give the event an ID\n options.action_call ||= `${this.id}_${action || 'action'}_${Math.floor(Math.random() * 9999)}`\n if (!this.graph) throw new NullGraphError()\n\n // @ts-expect-error deprecated\n this.graph.nodes_actioning[this.id] = action || 'actioning'\n this.onAction(action, param, options)\n // @ts-expect-error deprecated\n this.graph.nodes_actioning[this.id] = false\n\n // save execution/action ref\n if (options?.action_call) {\n this.action_call = options.action_call\n // @ts-expect-error deprecated\n this.graph.nodes_executedAction[this.id] = options.action_call\n }\n }\n // the nFrames it will be used (-- each step), means \"how old\" is the event\n this.action_triggered = 2\n this.onAfterExecuteNode?.(param, options)\n }\n\n /**\n * Triggers an event in this node, this will trigger any output with the same name\n * @param action name ( \"on_play\", ... ) if action is equivalent to false then the event is send to all\n */\n trigger(\n action: string,\n param: unknown,\n options: { action_call?: string }\n ): void {\n const { outputs } = this\n if (!outputs || !outputs.length) {\n return\n }\n\n if (this.graph) this.graph._last_trigger_time = LiteGraph.getTime()\n\n for (const [i, output] of outputs.entries()) {\n if (\n !output ||\n output.type !== LiteGraph.EVENT ||\n (action && output.name != action)\n ) {\n continue\n }\n this.triggerSlot(i, param, null, options)\n }\n }\n\n /**\n * Triggers a slot event in this node: cycle output slots and launch execute/action on connected nodes\n * @param slot the index of the output slot\n * @param link_id [optional] in case you want to trigger and specific output link in a slot\n */\n triggerSlot(\n slot: number,\n param: unknown,\n link_id: number | null,\n options?: { action_call?: string }\n ): void {\n options = options || {}\n if (!this.outputs) return\n\n if (slot == null) {\n console.error('slot must be a number')\n return\n }\n\n if (typeof slot !== 'number')\n console.warn(\n \"slot must be a number, use node.trigger('name') if you want to use a string\"\n )\n\n const output = this.outputs[slot]\n if (!output) return\n\n const links = output.links\n if (!links || !links.length) return\n\n if (!this.graph) throw new NullGraphError()\n this.graph._last_trigger_time = LiteGraph.getTime()\n\n // for every link attached here\n for (const id of links) {\n // to skip links\n if (link_id != null && link_id != id) continue\n\n const link_info = this.graph._links.get(id)\n // not connected\n if (!link_info) continue\n\n link_info._last_time = LiteGraph.getTime()\n const node = this.graph.getNodeById(link_info.target_id)\n // node not found?\n if (!node) continue\n\n if (node.mode === LGraphEventMode.ON_TRIGGER) {\n // generate unique trigger ID if not present\n if (!options.action_call)\n options.action_call = `${this.id}_trigg_${Math.floor(Math.random() * 9999)}`\n // -- wrapping node.onExecute(param); --\n node.doExecute?.(param, options)\n } else if (node.onAction) {\n // generate unique action ID if not present\n if (!options.action_call)\n options.action_call = `${this.id}_act_${Math.floor(Math.random() * 9999)}`\n // pass the action name\n const target_connection = node.inputs[link_info.target_slot]\n node.actionDo(target_connection.name, param, options)\n }\n }\n }\n\n /**\n * clears the trigger slot animation\n * @param slot the index of the output slot\n * @param link_id [optional] in case you want to trigger and specific output link in a slot\n */\n clearTriggeredSlot(slot: number, link_id: number): void {\n if (!this.outputs) return\n\n const output = this.outputs[slot]\n if (!output) return\n\n const links = output.links\n if (!links || !links.length) return\n\n if (!this.graph) throw new NullGraphError()\n\n // for every link attached here\n for (const id of links) {\n // to skip links\n if (link_id != null && link_id != id) continue\n\n const link_info = this.graph._links.get(id)\n // not connected\n if (!link_info) continue\n\n link_info._last_time = 0\n }\n }\n\n /**\n * changes node size and triggers callback\n */\n setSize(size: Size): void {\n this.size = size\n this.onResize?.(this.size)\n }\n\n /**\n * Expands the node size to fit its content.\n */\n expandToFitContent(): void {\n const newSize = this.computeSize()\n this.setSize([\n Math.max(this.size[0], newSize[0]),\n Math.max(this.size[1], newSize[1])\n ])\n }\n\n /**\n * add a new property to this node\n * @param type string defining the output type (\"vec3\",\"number\",...)\n * @param extra_info this can be used to have special properties of the property (like values, etc)\n */\n addProperty(\n name: string,\n default_value: NodeProperty | undefined,\n type?: string,\n extra_info?: Partial<INodePropertyInfo>\n ): INodePropertyInfo {\n const o: INodePropertyInfo = { name, type, default_value }\n if (extra_info) Object.assign(o, extra_info)\n\n this.properties_info ||= []\n this.properties_info.push(o)\n this.properties ||= {}\n this.properties[name] = default_value\n return o\n }\n\n /**\n * add a new output slot to use in this node\n * @param type string defining the output type (\"vec3\",\"number\",...)\n * @param extra_info this can be used to have special properties of an output (label, special color, position, etc)\n */\n addOutput<TProperties extends Partial<INodeOutputSlot>>(\n name: string,\n type: ISlotType,\n extra_info?: TProperties\n ): INodeOutputSlot & TProperties {\n const output = Object.assign(\n new NodeOutputSlot({ name, type, links: null }, this),\n extra_info\n )\n\n this.outputs ||= []\n this.outputs.push(output)\n this.onOutputAdded?.(output)\n\n if (LiteGraph.auto_load_slot_types)\n LiteGraph.registerNodeAndSlotType(this, type, true)\n\n this.expandToFitContent()\n this.setDirtyCanvas(true, true)\n return output\n }\n\n /**\n * remove an existing output slot\n */\n removeOutput(slot: number): void {\n // Only disconnect if node is part of a graph\n if (this.graph) {\n this.disconnectOutput(slot)\n }\n const { outputs } = this\n outputs.splice(slot, 1)\n\n for (let i = slot; i < outputs.length; ++i) {\n const output = outputs[i]\n if (!output || !output.links) continue\n\n // Only update link indices if node is part of a graph\n if (this.graph) {\n for (const linkId of output.links) {\n const link = this.graph._links.get(linkId)\n if (link) link.origin_slot--\n }\n }\n }\n\n this.onOutputRemoved?.(slot)\n this.setDirtyCanvas(true, true)\n }\n\n /**\n * add a new input slot to use in this node\n * @param type string defining the input type (\"vec3\",\"number\",...), it its a generic one use 0\n * @param extra_info this can be used to have special properties of an input (label, color, position, etc)\n */\n addInput<TProperties extends Partial<INodeInputSlot>>(\n name: string,\n type: ISlotType,\n extra_info?: TProperties\n ): INodeInputSlot & TProperties {\n type ||= 0\n\n const input = Object.assign(\n new NodeInputSlot({ name, type, link: null }, this),\n extra_info\n )\n\n this.inputs ||= []\n this.inputs.push(input)\n this.expandToFitContent()\n\n this.onInputAdded?.(input)\n LiteGraph.registerNodeAndSlotType(this, type)\n\n this.setDirtyCanvas(true, true)\n return input\n }\n\n /**\n * remove an existing input slot\n */\n removeInput(slot: number): void {\n // Only disconnect if node is part of a graph\n if (this.graph) {\n this.disconnectInput(slot, true)\n }\n const { inputs } = this\n const slot_info = inputs.splice(slot, 1)\n\n for (let i = slot; i < inputs.length; ++i) {\n const input = inputs[i]\n if (!input?.link) continue\n\n // Only update link indices if node is part of a graph\n if (this.graph) {\n const link = this.graph._links.get(input.link)\n if (link) link.target_slot--\n }\n }\n this.onInputRemoved?.(slot, slot_info[0])\n this.setDirtyCanvas(true, true)\n }\n\n /**\n * computes the minimum size of a node according to its inputs and output slots\n * @returns the total size\n */\n computeSize(out?: Size): Size {\n const ctorSize = this.constructor.size\n if (ctorSize) return [ctorSize[0], ctorSize[1]]\n\n const { inputs, outputs, widgets } = this\n let rows = Math.max(\n inputs ? inputs.filter((input) => !isWidgetInputSlot(input)).length : 1,\n outputs ? outputs.length : 1\n )\n const size = out ?? [0, 0]\n rows = Math.max(rows, 1)\n // although it should be graphcanvas.inner_text_font size\n const font_size = LiteGraph.NODE_TEXT_SIZE\n\n const padLeft = LiteGraph.NODE_TITLE_HEIGHT\n const padRight = padLeft * 0.33\n const title_width =\n padLeft + compute_text_size(this.title, this.titleFontStyle) + padRight\n let input_width = 0\n let widgetWidth = 0\n let output_width = 0\n\n if (inputs) {\n for (const input of inputs) {\n const text = input.label || input.localized_name || input.name || ''\n const text_width = compute_text_size(text, this.innerFontStyle)\n if (isWidgetInputSlot(input)) {\n const widget = this.getWidgetFromSlot(input)\n if (widget && !this.isWidgetVisible(widget)) continue\n\n if (text_width > widgetWidth) widgetWidth = text_width\n } else {\n if (text_width > input_width) input_width = text_width\n }\n }\n }\n\n if (outputs) {\n for (const output of outputs) {\n const text = output.label || output.localized_name || output.name || ''\n const text_width = compute_text_size(text, this.innerFontStyle)\n if (output_width < text_width) output_width = text_width\n }\n }\n\n const minWidth = LiteGraph.NODE_WIDTH * (widgets?.length ? 1.5 : 1)\n // Text + slot width + centre padding\n const centrePadding = input_width && output_width ? 5 : 0\n const slotsWidth =\n input_width +\n output_width +\n 2 * LiteGraph.NODE_SLOT_HEIGHT +\n centrePadding\n\n // Total distance from edge of node to the inner edge of the widget 'previous' arrow button\n const widgetMargin =\n BaseWidget.margin + BaseWidget.arrowMargin + BaseWidget.arrowWidth\n const widgetPadding = BaseWidget.minValueWidth + 2 * widgetMargin\n if (widgetWidth) widgetWidth += widgetPadding\n\n size[0] = Math.max(slotsWidth, widgetWidth, title_width, minWidth)\n size[1] =\n (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT\n\n // Get widget height & expand size if necessary\n let widgets_height = 0\n if (widgets?.length) {\n for (const widget of widgets) {\n if (!this.isWidgetVisible(widget)) continue\n\n let widget_height = 0\n if (widget.computeSize) {\n widget_height += widget.computeSize(size[0])[1]\n } else if (widget.computeLayoutSize) {\n // Expand widget width if necessary\n const { minHeight, minWidth } = widget.computeLayoutSize(this)\n const widgetWidth = minWidth + widgetPadding\n if (widgetWidth > size[0]) size[0] = widgetWidth\n\n widget_height += minHeight\n } else {\n widget_height += LiteGraph.NODE_WIDGET_HEIGHT\n }\n widgets_height += widget_height + 4\n }\n widgets_height += 8\n }\n\n // compute height using widgets height\n if (this.widgets_up) size[1] = Math.max(size[1], widgets_height)\n else if (this.widgets_start_y != null)\n size[1] = Math.max(size[1], widgets_height + this.widgets_start_y)\n else size[1] += widgets_height\n\n function compute_text_size(text: string, fontStyle: string) {\n return (\n LGraphCanvas._measureText?.(text, fontStyle) ??\n font_size * (text?.length ?? 0) * 0.6\n )\n }\n\n if (this.constructor.min_height && size[1] < this.constructor.min_height) {\n size[1] = this.constructor.min_height\n }\n\n // margin\n size[1] += 6\n\n return size\n }\n\n inResizeCorner(canvasX: number, canvasY: number): boolean {\n const rows = this.outputs ? this.outputs.length : 1\n const outputs_offset =\n (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT\n return isInRectangle(\n canvasX,\n canvasY,\n this.pos[0] + this.size[0] - 15,\n this.pos[1] + Math.max(this.size[1] - 15, outputs_offset),\n 20,\n 20\n )\n }\n\n /**\n * Returns which resize corner the point is over, if any.\n * @param canvasX X position in canvas coordinates\n * @param canvasY Y position in canvas coordinates\n * @returns The compass corner the point is in, otherwise `undefined`.\n */\n findResizeDirection(\n canvasX: number,\n canvasY: number\n ): CompassCorners | undefined {\n if (this.resizable === false) return\n\n const { boundingRect } = this\n if (!boundingRect.containsXy(canvasX, canvasY)) return\n\n // Check corners first (they take priority over edges)\n return boundingRect.findContainingCorner(\n canvasX,\n canvasY,\n LGraphNode.resizeHandleSize\n )\n }\n\n /**\n * returns all the info available about a property of this node.\n * @param property name of the property\n * @returns the object with all the available info\n */\n getPropertyInfo(property: string) {\n let info = null\n\n // there are several ways to define info about a property\n // legacy mode\n const { properties_info } = this\n if (properties_info) {\n for (const propInfo of properties_info) {\n if (propInfo.name == property) {\n info = propInfo\n break\n }\n }\n }\n // litescene mode using the constructor\n // @ts-expect-error deprecated https://github.com/Comfy-Org/litegraph.js/issues/639\n if (this.constructor[`@${property}`])\n // @ts-expect-error deprecated https://github.com/Comfy-Org/litegraph.js/issues/639\n info = this.constructor[`@${property}`]\n\n if (this.constructor.widgets_info?.[property])\n info = this.constructor.widgets_info[property]\n\n // litescene mode using the constructor\n if (!info && this.onGetPropertyInfo) {\n info = this.onGetPropertyInfo(property)\n }\n\n info ||= {}\n info.type ||= typeof this.properties[property]\n if (info.widget == 'combo') info.type = 'enum'\n\n return info\n }\n\n /**\n * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties\n * @param type the widget type\n * @param name the text to show on the widget\n * @param value the default value\n * @param callback function to call when it changes (optionally, it can be the name of the property to modify)\n * @param options the object that contains special properties of this widget\n * @returns the created widget object\n */\n addWidget<\n Type extends TWidgetType,\n TValue extends WidgetTypeMap[Type]['value']\n >(\n type: Type,\n name: string,\n value: TValue,\n callback: IBaseWidget['callback'] | string | null,\n options?: IWidgetOptions | string\n ): WidgetTypeMap[Type] | IBaseWidget {\n this.widgets ||= []\n\n if (!options && callback && typeof callback === 'object') {\n options = callback\n callback = null\n }\n\n // options can be the property name\n options ||= {}\n if (typeof options === 'string') options = { property: options }\n\n // callback can be the property name\n if (callback && typeof callback === 'string') {\n options.property = callback\n callback = null\n }\n\n const w: IBaseWidget & { type: Type } = {\n // @ts-expect-error - Type casting for widget type property\n type: type.toLowerCase(),\n name: name,\n value: value,\n callback: typeof callback !== 'function' ? undefined : callback,\n options,\n y: 0\n }\n\n if (w.options.y !== undefined) {\n w.y = w.options.y\n }\n\n if (!callback && !w.options.callback && !w.options.property) {\n console.warn(\n 'LiteGraph addWidget(...) without a callback or property assigned'\n )\n }\n if (type == 'combo' && !w.options.values) {\n throw \"LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }\"\n }\n\n const widget = this.addCustomWidget(w)\n this.expandToFitContent()\n return widget\n }\n\n addCustomWidget<TPlainWidget extends IBaseWidget>(\n custom_widget: TPlainWidget\n ): TPlainWidget | WidgetTypeMap[TPlainWidget['type']] {\n this.widgets ||= []\n const widget = toConcreteWidget(custom_widget, this, false) ?? custom_widget\n this.widgets.push(widget)\n return widget\n }\n\n addTitleButton(options: LGraphButtonOptions): LGraphButton {\n this.title_buttons ||= []\n const button = new LGraphButton(options)\n this.title_buttons.push(button)\n return button\n }\n\n onTitleButtonClick(button: LGraphButton, canvas: LGraphCanvas): void {\n // Dispatch event for button click\n canvas.dispatch('litegraph:node-title-button-clicked', {\n node: this,\n button: button\n })\n }\n\n removeWidgetByName(name: string): void {\n const widget = this.widgets?.find((x) => x.name === name)\n if (widget) this.removeWidget(widget)\n }\n\n removeWidget(widget: IBaseWidget): void {\n if (!this.widgets)\n throw new Error('removeWidget called on node without widgets')\n\n const widgetIndex = this.widgets.indexOf(widget)\n if (widgetIndex === -1) throw new Error('Widget not found on this node')\n\n // Clean up slot references to prevent memory leaks\n if (this.inputs) {\n for (const input of this.inputs) {\n if (input._widget === widget) {\n input._widget = undefined\n input.widget = undefined\n }\n }\n }\n\n widget.onRemove?.()\n this.widgets.splice(widgetIndex, 1)\n }\n\n ensureWidgetRemoved(widget: IBaseWidget): void {\n try {\n this.removeWidget(widget)\n } catch (error) {\n console.error('Failed to remove widget', error)\n }\n }\n\n move(deltaX: number, deltaY: number): void {\n if (this.pinned) return\n\n // If Vue nodes mode is enabled, skip LiteGraph's direct position update\n // The layout store will handle the movement and sync back to LiteGraph\n if (LiteGraph.vueNodesMode) {\n // Vue nodes handle their own dragging through the layout store\n // This prevents the snap-back issue from conflicting position updates\n return\n }\n\n this.pos[0] += deltaX\n this.pos[1] += deltaY\n }\n\n /**\n * Internal method to measure the node for rendering. Prefer {@link boundingRect} where possible.\n *\n * Populates {@link out} with the results in graph space.\n * Populates {@link _collapsed_width} with the collapsed width if the node is collapsed.\n * Adjusts for title and collapsed status, but does not call {@link onBounding}.\n * @param out `x, y, width, height` are written to this array.\n * @param ctx The canvas context to use for measuring text.\n */\n measure(out: Rect, ctx?: CanvasRenderingContext2D): void {\n const titleMode = this.title_mode\n const renderTitle =\n titleMode != TitleMode.TRANSPARENT_TITLE &&\n titleMode != TitleMode.NO_TITLE\n const titleHeight = renderTitle ? LiteGraph.NODE_TITLE_HEIGHT : 0\n\n out[0] = this.pos[0]\n out[1] = this.pos[1] + -titleHeight\n if (!this.flags?.collapsed) {\n out[2] = this.size[0]\n out[3] = this.size[1] + titleHeight\n } else {\n if (ctx) ctx.font = this.innerFontStyle\n this._collapsed_width = Math.min(\n this.size[0],\n ctx\n ? ctx.measureText(this.getTitle() ?? '').width +\n LiteGraph.NODE_TITLE_HEIGHT * 2\n : 0\n )\n out[2] = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH\n out[3] = LiteGraph.NODE_TITLE_HEIGHT\n }\n }\n\n /**\n * returns the bounding of the object, used for rendering purposes\n * @param out {Rect?} [optional] a place to store the output, to free garbage\n * @param includeExternal {boolean?} [optional] set to true to\n * include the shadow and connection points in the bounding calculation\n * @returns the bounding box in format of [topleft_cornerx, topleft_cornery, width, height]\n */\n getBounding(out?: Rect, includeExternal?: boolean): Rect {\n out ||= [0, 0, 0, 0]\n\n const rect = includeExternal ? this.renderArea : this.boundingRect\n out[0] = rect[0]\n out[1] = rect[1]\n out[2] = rect[2]\n out[3] = rect[3]\n\n return out\n }\n\n /**\n * Calculates the render area of this node, populating both {@link boundingRect} and {@link renderArea}.\n * Called automatically at the start of every frame.\n */\n updateArea(ctx?: CanvasRenderingContext2D): void {\n const bounds = this.#boundingRect\n this.measure(bounds, ctx)\n this.onBounding?.(bounds)\n\n const renderArea = this.#renderArea\n renderArea.set(bounds)\n // 4 offset for collapsed node connection points\n renderArea[0] -= 4\n renderArea[1] -= 4\n // Add shadow & left offset\n renderArea[2] += 6 + 4\n // Add shadow & top offsets\n renderArea[3] += 5 + 4\n }\n\n /**\n * checks if a point is inside the shape of a node\n */\n isPointInside(x: number, y: number): boolean {\n return isInRect(x, y, this.boundingRect)\n }\n\n /**\n * Checks if the provided point is inside this node's collapse button area.\n * @param x X co-ordinate to check\n * @param y Y co-ordinate to check\n * @returns true if the x,y point is in the collapse button area, otherwise false\n */\n isPointInCollapse(x: number, y: number): boolean {\n const squareLength = LiteGraph.NODE_TITLE_HEIGHT\n return isInRectangle(\n x,\n y,\n this.pos[0],\n this.pos[1] - squareLength,\n squareLength,\n squareLength\n )\n }\n\n /**\n * Returns the input slot at the given position. Uses full 20 height, and approximates the label length.\n * @param pos The graph co-ordinates to check\n * @returns The input slot at the given position if found, otherwise `undefined`.\n */\n getInputOnPos(pos: Point): INodeInputSlot | undefined {\n return getNodeInputOnPos(this, pos[0], pos[1])?.input\n }\n\n /**\n * Returns the output slot at the given position. Uses full 20x20 box for the slot.\n * @param pos The graph co-ordinates to check\n * @returns The output slot at the given position if found, otherwise `undefined`.\n */\n getOutputOnPos(pos: Point): INodeOutputSlot | undefined {\n return getNodeOutputOnPos(this, pos[0], pos[1])?.output\n }\n\n /**\n * Returns the input or output slot at the given position.\n *\n * Tries {@link getNodeInputOnPos} first, then {@link getNodeOutputOnPos}.\n * @param pos The graph co-ordinates to check\n * @returns The input or output slot at the given position if found, otherwise `undefined`.\n */\n getSlotOnPos(pos: Point): INodeInputSlot | INodeOutputSlot | undefined {\n if (!isPointInRect(pos, this.boundingRect)) return\n\n return this.getInputOnPos(pos) ?? this.getOutputOnPos(pos)\n }\n\n /**\n * @deprecated Use {@link getSlotOnPos} instead.\n * checks if a point is inside a node slot, and returns info about which slot\n * @param x\n * @param y\n * @returns if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] }\n */\n getSlotInPosition(x: number, y: number): IFoundSlot | null {\n // search for inputs\n const { inputs, outputs } = this\n\n if (inputs) {\n for (const [i, input] of inputs.entries()) {\n const pos = this.getInputPos(i)\n if (isInRectangle(x, y, pos[0] - 10, pos[1] - 10, 20, 20)) {\n return { input, slot: i, link_pos: pos }\n }\n }\n }\n\n if (outputs) {\n for (const [i, output] of outputs.entries()) {\n const pos = this.getOutputPos(i)\n if (isInRectangle(x, y, pos[0] - 10, pos[1] - 10, 20, 20)) {\n return { output, slot: i, link_pos: pos }\n }\n }\n }\n\n return null\n }\n\n /**\n * Gets the widget on this node at the given co-ordinates.\n * @param canvasX X co-ordinate in graph space\n * @param canvasY Y co-ordinate in graph space\n * @returns The widget found, otherwise `null`\n */\n getWidgetOnPos(\n canvasX: number,\n canvasY: number,\n includeDisabled = false\n ): IBaseWidget | undefined {\n const { widgets, pos, size } = this\n if (!widgets?.length) return\n\n const x = canvasX - pos[0]\n const y = canvasY - pos[1]\n const nodeWidth = size[0]\n\n for (const widget of widgets) {\n if (\n (widget.computedDisabled && !includeDisabled) ||\n !this.isWidgetVisible(widget)\n ) {\n continue\n }\n\n const h =\n widget.computedHeight ??\n widget.computeSize?.(nodeWidth)[1] ??\n LiteGraph.NODE_WIDGET_HEIGHT\n\n const maybeDOMWidget = widget as { margin?: number }\n const mtop = maybeDOMWidget.margin ?? -2\n const mbot = maybeDOMWidget.margin ?? 2\n const mx = maybeDOMWidget.margin ?? 6\n\n const w = widget.width || nodeWidth\n if (\n widget.last_y !== undefined &&\n isInRectangle(\n x,\n y,\n mx,\n widget.last_y + mtop,\n w - 2 * mx,\n h - mtop - mbot\n )\n ) {\n return widget\n }\n }\n }\n\n /**\n * Returns the input slot with a given name (used for dynamic slots), -1 if not found\n * @param name the name of the slot to find\n * @param returnObj if the obj itself wanted\n * @returns the slot (-1 if not found)\n */\n findInputSlot<TReturn extends false>(\n name: string,\n returnObj?: TReturn\n ): number\n findInputSlot<TReturn extends true>(\n name: string,\n returnObj?: TReturn\n ): INodeInputSlot\n findInputSlot(name: string, returnObj: boolean = false) {\n const { inputs } = this\n if (!inputs) return -1\n\n for (const [i, input] of inputs.entries()) {\n if (name == input.name) {\n return !returnObj ? i : input\n }\n }\n return -1\n }\n\n /**\n * returns the output slot with a given name (used for dynamic slots), -1 if not found\n * @param name the name of the slot to find\n * @param returnObj if the obj itself wanted\n * @returns the slot (-1 if not found)\n */\n findOutputSlot<TReturn extends false>(\n name: string,\n returnObj?: TReturn\n ): number\n findOutputSlot<TReturn extends true>(\n name: string,\n returnObj?: TReturn\n ): INodeOutputSlot\n findOutputSlot(name: string, returnObj: boolean = false) {\n const { outputs } = this\n if (!outputs) return -1\n\n for (const [i, output] of outputs.entries()) {\n if (name == output.name) {\n return !returnObj ? i : output\n }\n }\n return -1\n }\n\n /**\n * Finds the first free input slot.\n * @param optsIn\n * @returns The index of the first matching slot, the slot itself if returnObj is true, or -1 if not found.\n */\n findInputSlotFree<TReturn extends false>(\n optsIn?: FindFreeSlotOptions & { returnObj?: TReturn }\n ): number\n findInputSlotFree<TReturn extends true>(\n optsIn?: FindFreeSlotOptions & { returnObj?: TReturn }\n ): INodeInputSlot | -1\n findInputSlotFree(optsIn?: FindFreeSlotOptions) {\n return this.#findFreeSlot(this.inputs, optsIn)\n }\n\n /**\n * Finds the first free output slot.\n * @param optsIn\n * @returns The index of the first matching slot, the slot itself if returnObj is true, or -1 if not found.\n */\n findOutputSlotFree<TReturn extends false>(\n optsIn?: FindFreeSlotOptions & { returnObj?: TReturn }\n ): number\n findOutputSlotFree<TReturn extends true>(\n optsIn?: FindFreeSlotOptions & { returnObj?: TReturn }\n ): INodeOutputSlot | -1\n findOutputSlotFree(optsIn?: FindFreeSlotOptions) {\n return this.#findFreeSlot(this.outputs, optsIn)\n }\n\n /**\n * Finds the next free slot\n * @param slots The slots to search, i.e. this.inputs or this.outputs\n */\n #findFreeSlot<TSlot extends INodeInputSlot | INodeOutputSlot>(\n slots: TSlot[],\n options?: FindFreeSlotOptions\n ): TSlot | number {\n const defaults = {\n returnObj: false,\n typesNotAccepted: []\n }\n const opts = Object.assign(defaults, options || {})\n const length = slots?.length\n if (!(length > 0)) return -1\n\n for (let i = 0; i < length; ++i) {\n const slot: TSlot & IGenericLinkOrLinks = slots[i]\n if (!slot || slot.link || slot.links?.length) continue\n if (opts.typesNotAccepted?.includes?.(slot.type)) continue\n return !opts.returnObj ? i : slot\n }\n return -1\n }\n\n /**\n * findSlotByType for INPUTS\n */\n findInputSlotByType<TReturn extends false>(\n type: ISlotType,\n returnObj?: TReturn,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): number\n findInputSlotByType<TReturn extends true>(\n type: ISlotType,\n returnObj?: TReturn,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): INodeInputSlot\n findInputSlotByType(\n type: ISlotType,\n returnObj?: boolean,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ) {\n return this.#findSlotByType(\n this.inputs,\n type,\n returnObj,\n preferFreeSlot,\n doNotUseOccupied\n )\n }\n\n /**\n * findSlotByType for OUTPUTS\n */\n findOutputSlotByType<TReturn extends false>(\n type: ISlotType,\n returnObj?: TReturn,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): number\n findOutputSlotByType<TReturn extends true>(\n type: ISlotType,\n returnObj?: TReturn,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): INodeOutputSlot\n findOutputSlotByType(\n type: ISlotType,\n returnObj?: boolean,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ) {\n return this.#findSlotByType(\n this.outputs,\n type,\n returnObj,\n preferFreeSlot,\n doNotUseOccupied\n )\n }\n\n /**\n * returns the output (or input) slot with a given type, -1 if not found\n * @param input use inputs instead of outputs\n * @param type the type of the slot to find\n * @param returnObj if the obj itself wanted\n * @param preferFreeSlot if we want a free slot (if not found, will return the first of the type anyway)\n * @returns the slot (-1 if not found)\n */\n findSlotByType<TSlot extends true | false, TReturn extends false>(\n input: TSlot,\n type: ISlotType,\n returnObj?: TReturn,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): number\n findSlotByType<TSlot extends true, TReturn extends true>(\n input: TSlot,\n type: ISlotType,\n returnObj?: TReturn,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): INodeInputSlot | -1\n findSlotByType<TSlot extends false, TReturn extends true>(\n input: TSlot,\n type: ISlotType,\n returnObj?: TReturn,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): INodeOutputSlot | -1\n findSlotByType(\n input: boolean,\n type: ISlotType,\n returnObj?: boolean,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): number | INodeOutputSlot | INodeInputSlot {\n return input\n ? this.#findSlotByType(\n this.inputs,\n type,\n returnObj,\n preferFreeSlot,\n doNotUseOccupied\n )\n : this.#findSlotByType(\n this.outputs,\n type,\n returnObj,\n preferFreeSlot,\n doNotUseOccupied\n )\n }\n\n /**\n * Finds a matching slot from those provided, returning the slot itself or its index in {@link slots}.\n * @param slots Slots to search (this.inputs or this.outputs)\n * @param type Type of slot to look for\n * @param returnObj If true, returns the slot itself. Otherwise, the index.\n * @param preferFreeSlot Prefer a free slot, but if none are found, fall back to an occupied slot.\n * @param doNotUseOccupied Do not fall back to occupied slots.\n * @see {findSlotByType}\n * @see {findOutputSlotByType}\n * @see {findInputSlotByType}\n * @returns If a match is found, the slot if returnObj is true, otherwise the index. If no matches are found, -1\n */\n #findSlotByType<TSlot extends INodeInputSlot | INodeOutputSlot>(\n slots: TSlot[],\n type: ISlotType,\n returnObj?: boolean,\n preferFreeSlot?: boolean,\n doNotUseOccupied?: boolean\n ): TSlot | number {\n const length = slots?.length\n if (!length) return -1\n\n // Empty string and * match anything (type: 0)\n if (type == '' || type == '*') type = 0\n const sourceTypes = String(type).toLowerCase().split(',')\n\n // Run the search\n let occupiedSlot: number | TSlot | null = null\n for (let i = 0; i < length; ++i) {\n const slot: TSlot & IGenericLinkOrLinks = slots[i]\n const destTypes =\n slot.type == '0' || slot.type == '*'\n ? ['0']\n : String(slot.type).toLowerCase().split(',')\n\n for (const sourceType of sourceTypes) {\n // TODO: Remove _event_ entirely.\n const source = sourceType == '_event_' ? LiteGraph.EVENT : sourceType\n\n for (const destType of destTypes) {\n const dest = destType == '_event_' ? LiteGraph.EVENT : destType\n\n if (source == dest || source === '*' || dest === '*') {\n if (preferFreeSlot && (slot.links?.length || slot.link != null)) {\n // In case we can't find a free slot.\n occupiedSlot ??= returnObj ? slot : i\n continue\n }\n return returnObj ? slot : i\n }\n }\n }\n }\n\n return doNotUseOccupied ? -1 : (occupiedSlot ?? -1)\n }\n\n /**\n * Determines the slot index to connect to when attempting to connect by type.\n * @param findInputs If true, searches for an input. Otherwise, an output.\n * @param node The node at the other end of the connection.\n * @param slotType The type of slot at the other end of the connection.\n * @param options Search restrictions to adhere to.\n * @see {connectByType}\n * @see {connectByTypeOutput}\n */\n findConnectByTypeSlot(\n findInputs: boolean,\n node: LGraphNode,\n slotType: ISlotType,\n options?: ConnectByTypeOptions\n ): number | undefined {\n // LEGACY: Old options names\n if (options && typeof options === 'object') {\n if ('firstFreeIfInputGeneralInCase' in options)\n options.wildcardToTyped = !!options.firstFreeIfInputGeneralInCase\n if ('firstFreeIfOutputGeneralInCase' in options)\n options.wildcardToTyped = !!options.firstFreeIfOutputGeneralInCase\n if ('generalTypeInCase' in options)\n options.typedToWildcard = !!options.generalTypeInCase\n }\n const optsDef: ConnectByTypeOptions = {\n createEventInCase: true,\n wildcardToTyped: true,\n typedToWildcard: true\n }\n const opts = Object.assign(optsDef, options)\n\n if (!this.graph) throw new NullGraphError()\n\n if (node && typeof node === 'number') {\n const nodeById = this.graph.getNodeById(node)\n if (!nodeById) return\n\n node = nodeById\n }\n const slot = node.findSlotByType(findInputs, slotType, false, true)\n if (slot >= 0 && slot !== null) return slot\n\n // TODO: Remove or reimpl. events. WILL CREATE THE onTrigger IN SLOT\n if (opts.createEventInCase && slotType == LiteGraph.EVENT) {\n if (findInputs) return -1\n if (LiteGraph.do_add_triggers_slots) return node.addOnExecutedOutput()\n }\n\n // connect to the first general output slot if not found a specific type and\n if (opts.typedToWildcard) {\n const generalSlot = node.findSlotByType(findInputs, 0, false, true, true)\n if (generalSlot >= 0) return generalSlot\n }\n // connect to the first free input slot if not found a specific type and this output is general\n if (\n opts.wildcardToTyped &&\n (slotType == 0 || slotType == '*' || slotType == '')\n ) {\n const opt = { typesNotAccepted: [LiteGraph.EVENT] }\n const nonEventSlot = findInputs\n ? node.findInputSlotFree(opt)\n : node.findOutputSlotFree(opt)\n if (nonEventSlot >= 0) return nonEventSlot\n }\n }\n\n /**\n * Finds the first free output slot with any of the comma-delimited types in {@link type}.\n *\n * If no slots are free, falls back in order to:\n * - The first free wildcard slot\n * - The first occupied slot\n * - The first occupied wildcard slot\n * @param type The {@link ISlotType type} of slot to find\n * @returns The index and slot if found, otherwise `undefined`.\n */\n findOutputByType(\n type: ISlotType\n ): { index: number; slot: INodeOutputSlot } | undefined {\n return findFreeSlotOfType(\n this.outputs,\n type,\n (output) => !output.links?.length\n )\n }\n\n /**\n * Finds the first free input slot with any of the comma-delimited types in {@link type}.\n *\n * If no slots are free, falls back in order to:\n * - The first free wildcard slot\n * - The first occupied slot\n * - The first occupied wildcard slot\n * @param type The {@link ISlotType type} of slot to find\n * @returns The index and slot if found, otherwise `undefined`.\n */\n findInputByType(\n type: ISlotType\n ): { index: number; slot: INodeInputSlot } | undefined {\n return findFreeSlotOfType(\n this.inputs,\n type,\n (input) =>\n input.link == null || !!this.graph?.getLink(input.link)?._dragging\n )\n }\n\n /**\n * connect this node output to the input of another node BY TYPE\n * @param slot (could be the number of the slot or the string with the name of the slot)\n * @param target_node the target node\n * @param target_slotType the input slot type of the target node\n * @returns the link_info is created, otherwise null\n */\n connectByType(\n slot: number | string,\n target_node: LGraphNode,\n target_slotType: ISlotType,\n optsIn?: ConnectByTypeOptions\n ): LLink | null {\n const slotIndex = this.findConnectByTypeSlot(\n true,\n target_node,\n target_slotType,\n optsIn\n )\n if (slotIndex !== undefined)\n return this.connect(slot, target_node, slotIndex, optsIn?.afterRerouteId)\n\n // No compatible slot found - connection not possible\n return null\n }\n\n /**\n * connect this node input to the output of another node BY TYPE\n * @param slot (could be the number of the slot or the string with the name of the slot)\n * @param source_node the target node\n * @param source_slotType the output slot type of the target node\n * @returns the link_info is created, otherwise null\n */\n connectByTypeOutput(\n slot: number | string,\n source_node: LGraphNode,\n source_slotType: ISlotType,\n optsIn?: ConnectByTypeOptions\n ): LLink | null {\n // LEGACY: Old options names\n if (typeof optsIn === 'object') {\n if ('firstFreeIfInputGeneralInCase' in optsIn)\n optsIn.wildcardToTyped = !!optsIn.firstFreeIfInputGeneralInCase\n if ('generalTypeInCase' in optsIn)\n optsIn.typedToWildcard = !!optsIn.generalTypeInCase\n }\n const slotIndex = this.findConnectByTypeSlot(\n false,\n source_node,\n source_slotType,\n optsIn\n )\n if (slotIndex !== undefined)\n return source_node.connect(slotIndex, this, slot, optsIn?.afterRerouteId)\n\n console.error(\n '[connectByType]: no way to connect type:',\n source_slotType,\n 'to node:',\n source_node\n )\n return null\n }\n\n canConnectTo(\n node: NodeLike,\n toSlot: INodeInputSlot | SubgraphIO,\n fromSlot: INodeOutputSlot | SubgraphIO\n ) {\n return (\n this.id !== node.id &&\n LiteGraph.isValidConnection(fromSlot.type, toSlot.type)\n )\n }\n\n /**\n * Connect an output of this node to an input of another node\n * @param slot (could be the number of the slot or the string with the name of the slot)\n * @param target_node the target node\n * @param target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)\n * @returns the link_info is created, otherwise null\n */\n connect(\n slot: number | string,\n target_node: LGraphNode,\n target_slot: ISlotType,\n afterRerouteId?: RerouteId\n ): LLink | null {\n // Allow legacy API support for searching target_slot by string, without mutating the input variables\n let targetIndex: number | null\n\n const { graph, outputs } = this\n if (!graph) {\n // could be connected before adding it to a graph\n // due to link ids being associated with graphs\n console.error(\n \"Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them.\"\n )\n return null\n }\n\n // seek for the output slot\n if (typeof slot === 'string') {\n slot = this.findOutputSlot(slot)\n if (slot == -1) {\n if (LiteGraph.debug)\n console.error(`Connect: Error, no slot of name ${slot}`)\n return null\n }\n } else if (!outputs || slot >= outputs.length) {\n if (LiteGraph.debug)\n console.error('Connect: Error, slot number not found')\n return null\n }\n\n if (target_node && typeof target_node === 'number') {\n const nodeById = graph.getNodeById(target_node)\n if (!nodeById) throw 'target node is null'\n\n target_node = nodeById\n }\n if (!target_node) throw 'target node is null'\n\n // avoid loopback\n if (target_node == this) return null\n\n // you can specify the slot by name\n if (typeof target_slot === 'string') {\n targetIndex = target_node.findInputSlot(target_slot)\n if (targetIndex == -1) {\n if (LiteGraph.debug)\n console.error(`Connect: Error, no slot of name ${targetIndex}`)\n return null\n }\n } else if (target_slot === LiteGraph.EVENT) {\n // TODO: Events\n if (LiteGraph.do_add_triggers_slots) {\n target_node.changeMode(LGraphEventMode.ON_TRIGGER)\n targetIndex = target_node.findInputSlot('onTrigger')\n } else {\n return null\n }\n } else if (typeof target_slot === 'number') {\n targetIndex = target_slot\n } else {\n targetIndex = 0\n }\n\n // Allow target node to change slot\n if (target_node.onBeforeConnectInput) {\n // This way node can choose another slot (or make a new one?)\n const requestedIndex = target_node.onBeforeConnectInput(\n targetIndex,\n target_slot\n )\n targetIndex = typeof requestedIndex === 'number' ? requestedIndex : null\n }\n\n if (\n targetIndex === null ||\n !target_node.inputs ||\n targetIndex >= target_node.inputs.length\n ) {\n if (LiteGraph.debug)\n console.error('Connect: Error, slot number not found')\n return null\n }\n\n const input = target_node.inputs[targetIndex]\n const output = outputs[slot]\n\n if (!output) return null\n\n if (output.links?.length) {\n if (\n output.type === LiteGraph.EVENT &&\n !LiteGraph.allow_multi_output_for_events\n ) {\n graph.beforeChange()\n this.disconnectOutput(slot)\n }\n }\n\n const link = this.connectSlots(output, target_node, input, afterRerouteId)\n return link ?? null\n }\n\n /**\n * Connect two slots between two nodes\n * @param output The output slot to connect\n * @param inputNode The node that the input slot is on\n * @param input The input slot to connect\n * @param afterRerouteId The reroute ID to use for the link\n * @returns The link that was created, or null if the connection was blocked\n */\n connectSlots(\n output: INodeOutputSlot,\n inputNode: LGraphNode,\n input: INodeInputSlot,\n afterRerouteId: RerouteId | undefined\n ): LLink | null | undefined {\n const { graph } = this\n if (!graph) throw new NullGraphError()\n\n const layoutMutations = useLayoutMutations()\n\n const outputIndex = this.outputs.indexOf(output)\n if (outputIndex === -1) {\n console.warn('connectSlots: output not found')\n return\n }\n const inputIndex = inputNode.inputs.indexOf(input)\n if (inputIndex === -1) {\n console.warn('connectSlots: input not found')\n return\n }\n\n // check targetSlot and check connection types\n if (!LiteGraph.isValidConnection(output.type, input.type)) {\n this.setDirtyCanvas(false, true)\n return null\n }\n\n // Allow nodes to block connection\n if (\n inputNode.onConnectInput?.(\n inputIndex,\n output.type,\n output,\n this,\n outputIndex\n ) === false\n )\n return null\n if (\n this.onConnectOutput?.(\n outputIndex,\n input.type,\n input,\n inputNode,\n inputIndex\n ) === false\n )\n return null\n\n // if there is something already plugged there, disconnect\n if (inputNode.inputs[inputIndex]?.link != null) {\n graph.beforeChange()\n inputNode.disconnectInput(inputIndex, true)\n }\n\n const maybeCommonType =\n input.type && output.type && commonType(input.type, output.type)\n\n const link = new LLink(\n ++graph.state.lastLinkId,\n maybeCommonType || input.type || output.type,\n this.id,\n outputIndex,\n inputNode.id,\n inputIndex,\n afterRerouteId\n )\n\n // add to graph links list\n graph._links.set(link.id, link)\n\n // Register link in Layout Store for spatial tracking\n layoutMutations.setSource(LayoutSource.Canvas)\n layoutMutations.createLink(\n link.id,\n this.id,\n outputIndex,\n inputNode.id,\n inputIndex\n )\n\n // connect in output\n output.links ??= []\n output.links.push(link.id)\n // connect in input\n const targetInput = inputNode.inputs[inputIndex]\n targetInput.link = link.id\n if (targetInput.widget) {\n graph.trigger('node:slot-links:changed', {\n nodeId: inputNode.id,\n slotType: NodeSlotType.INPUT,\n slotIndex: inputIndex,\n connected: true,\n linkId: link.id\n })\n }\n\n // Reroutes\n const reroutes = LLink.getReroutes(graph, link)\n for (const reroute of reroutes) {\n reroute.linkIds.add(link.id)\n if (reroute.floating) reroute.floating = undefined\n reroute._dragging = undefined\n }\n\n // If this is the terminus of a floating link, remove it\n const lastReroute = reroutes.at(-1)\n if (lastReroute) {\n for (const linkId of lastReroute.floatingLinkIds) {\n const link = graph.floatingLinks.get(linkId)\n if (link?.parentId === lastReroute.id) {\n graph.removeFloatingLink(link)\n }\n }\n }\n graph._version++\n\n // link has been created now, so its updated\n this.onConnectionsChange?.(\n NodeSlotType.OUTPUT,\n outputIndex,\n true,\n link,\n output\n )\n\n inputNode.onConnectionsChange?.(\n NodeSlotType.INPUT,\n inputIndex,\n true,\n link,\n input\n )\n\n this.setDirtyCanvas(false, true)\n graph.afterChange()\n\n return link\n }\n\n connectFloatingReroute(\n pos: Point,\n slot: INodeInputSlot | INodeOutputSlot,\n afterRerouteId?: RerouteId\n ): Reroute {\n const { graph, id } = this\n if (!graph) throw new NullGraphError()\n\n // Assertion: It's either there or it isn't.\n const inputIndex = this.inputs.indexOf(slot as INodeInputSlot)\n const outputIndex = this.outputs.indexOf(slot as INodeOutputSlot)\n if (inputIndex === -1 && outputIndex === -1) throw new Error('Invalid slot')\n\n const slotType = outputIndex === -1 ? 'input' : 'output'\n\n const reroute = graph.setReroute({\n pos,\n parentId: afterRerouteId,\n linkIds: [],\n floating: { slotType }\n })\n\n const parentReroute = graph.getReroute(afterRerouteId)\n const fromLastFloatingReroute =\n parentReroute?.floating?.slotType === 'output'\n\n // Adding from an output, or a floating reroute that is NOT the tip of an existing floating chain\n if (afterRerouteId == null || !fromLastFloatingReroute) {\n const link = new LLink(\n -1,\n slot.type,\n outputIndex === -1 ? -1 : id,\n outputIndex,\n inputIndex === -1 ? -1 : id,\n inputIndex\n )\n link.parentId = reroute.id\n graph.addFloatingLink(link)\n return reroute\n }\n\n // Adding a new floating reroute from the tip of a floating chain.\n if (!parentReroute)\n throw new Error('[connectFloatingReroute] Parent reroute not found')\n\n const link = parentReroute.getFloatingLinks('output')?.[0]\n if (!link)\n throw new Error('[connectFloatingReroute] Floating link not found')\n\n reroute.floatingLinkIds.add(link.id)\n link.parentId = reroute.id\n parentReroute.floating = undefined\n return reroute\n }\n\n /**\n * disconnect one output to an specific node\n * @param slot (could be the number of the slot or the string with the name of the slot)\n * @param target_node the target node to which this slot is connected [Optional,\n * if not target_node is specified all nodes will be disconnected]\n * @returns if it was disconnected successfully\n */\n disconnectOutput(slot: string | number, target_node?: LGraphNode): boolean {\n if (typeof slot === 'string') {\n slot = this.findOutputSlot(slot)\n if (slot == -1) {\n if (LiteGraph.debug)\n console.error(`Connect: Error, no slot of name ${slot}`)\n return false\n }\n } else if (!this.outputs || slot >= this.outputs.length) {\n if (LiteGraph.debug)\n console.error('Connect: Error, slot number not found')\n return false\n }\n\n // get output slot\n const output = this.outputs[slot]\n if (!output) return false\n\n if (output._floatingLinks) {\n for (const link of output._floatingLinks) {\n if (link.hasOrigin(this.id, slot)) {\n this.graph?.removeFloatingLink(link)\n }\n }\n }\n\n if (!output.links || output.links.length == 0) return false\n const { links } = output\n\n // one of the output links in this slot\n const graph = this.graph\n if (!graph) throw new NullGraphError()\n\n if (target_node) {\n const target =\n typeof target_node === 'number'\n ? graph.getNodeById(target_node)\n : target_node\n if (!target) throw 'Target Node not found'\n\n for (const [i, link_id] of links.entries()) {\n const link_info = graph._links.get(link_id)\n if (link_info?.target_id != target.id) continue\n\n // is the link we are searching for...\n // remove here\n links.splice(i, 1)\n const input = target.inputs[link_info.target_slot]\n // remove there\n input.link = null\n if (input.widget) {\n graph.trigger('node:slot-links:changed', {\n nodeId: target.id,\n slotType: NodeSlotType.INPUT,\n slotIndex: link_info.target_slot,\n connected: false,\n linkId: link_info.id\n })\n }\n\n // remove the link from the links pool\n link_info.disconnect(graph, 'input')\n graph._version++\n\n // link_info hasn't been modified so its ok\n target.onConnectionsChange?.(\n NodeSlotType.INPUT,\n link_info.target_slot,\n false,\n link_info,\n input\n )\n this.onConnectionsChange?.(\n NodeSlotType.OUTPUT,\n slot,\n false,\n link_info,\n output\n )\n\n break\n }\n } else {\n // all the links in this output slot\n for (const link_id of links) {\n const link_info = graph._links.get(link_id)\n if (!link_info) continue\n if (\n link_info.target_id === SUBGRAPH_OUTPUT_ID &&\n graph instanceof Subgraph\n ) {\n const targetSlot = graph.outputNode.slots[link_info.target_slot]\n if (targetSlot) {\n targetSlot.linkIds.length = 0\n } else {\n console.error('Missing subgraphOutput slot when disconnecting link')\n }\n }\n\n const target = graph.getNodeById(link_info.target_id)\n graph._version++\n\n if (target) {\n const input = target.inputs[link_info.target_slot]\n // remove other side link\n input.link = null\n if (input.widget) {\n graph.trigger('node:slot-links:changed', {\n nodeId: target.id,\n slotType: NodeSlotType.INPUT,\n slotIndex: link_info.target_slot,\n connected: false,\n linkId: link_info.id\n })\n }\n\n // link_info hasn't been modified so its ok\n target.onConnectionsChange?.(\n NodeSlotType.INPUT,\n link_info.target_slot,\n false,\n link_info,\n input\n )\n }\n // remove the link from the links pool\n link_info.disconnect(graph, 'input')\n\n this.onConnectionsChange?.(\n NodeSlotType.OUTPUT,\n slot,\n false,\n link_info,\n output\n )\n }\n output.links = null\n }\n\n this.setDirtyCanvas(false, true)\n return true\n }\n\n /**\n * Disconnect one input\n * @param slot Input slot index, or the name of the slot\n * @param keepReroutes If `true`, reroutes will not be garbage collected.\n * @returns true if disconnected successfully or already disconnected, otherwise false\n */\n disconnectInput(slot: number | string, keepReroutes?: boolean): boolean {\n // Allow search by string\n if (typeof slot === 'string') {\n slot = this.findInputSlot(slot)\n if (slot == -1) {\n if (LiteGraph.debug)\n console.error(`Connect: Error, no slot of name ${slot}`)\n return false\n }\n } else if (!this.inputs || slot >= this.inputs.length) {\n if (LiteGraph.debug) {\n console.error('Connect: Error, slot number not found')\n }\n return false\n }\n\n const input = this.inputs[slot]\n if (!input) {\n console.error('disconnectInput: input not found', slot, this.inputs)\n return false\n }\n\n const { graph } = this\n if (!graph) throw new NullGraphError()\n\n // Break floating links\n if (input._floatingLinks?.size) {\n for (const link of input._floatingLinks) {\n graph.removeFloatingLink(link)\n }\n }\n\n const link_id = this.inputs[slot].link\n if (link_id != null) {\n this.inputs[slot].link = null\n if (input.widget) {\n graph.trigger('node:slot-links:changed', {\n nodeId: this.id,\n slotType: NodeSlotType.INPUT,\n slotIndex: slot,\n connected: false,\n linkId: link_id\n })\n }\n\n // remove other side\n const link_info = graph._links.get(link_id)\n if (link_info) {\n // Let SubgraphInput do the disconnect.\n if (link_info.origin_id === -10 && 'inputNode' in graph) {\n graph.inputNode._disconnectNodeInput(this, input, link_info)\n return true\n }\n\n const target_node = graph.getNodeById(link_info.origin_id)\n if (!target_node) {\n console.error(\n 'disconnectInput: output not found',\n link_info.origin_slot\n )\n return false\n }\n\n const output = target_node.outputs[link_info.origin_slot]\n if (!output?.links?.length) {\n // Output not found - may have been removed\n return false\n }\n\n // search in the inputs list for this link\n let i = 0\n for (const l = output.links.length; i < l; i++) {\n if (output.links[i] == link_id) {\n output.links.splice(i, 1)\n break\n }\n }\n\n link_info.disconnect(graph, keepReroutes ? 'output' : undefined)\n if (graph) graph._version++\n\n this.onConnectionsChange?.(\n NodeSlotType.INPUT,\n slot,\n false,\n link_info,\n input\n )\n target_node.onConnectionsChange?.(\n NodeSlotType.OUTPUT,\n i,\n false,\n link_info,\n output\n )\n }\n }\n\n this.setDirtyCanvas(false, true)\n return true\n }\n\n /**\n * @deprecated Use {@link getInputPos} or {@link getOutputPos} instead.\n * returns the center of a connection point in canvas coords\n * @param is_input true if if a input slot, false if it is an output\n * @param slot_number (could be the number of the slot or the string with the name of the slot)\n * @param out [optional] a place to store the output, to free garbage\n * @returns the position\n */\n getConnectionPos(is_input: boolean, slot_number: number, out?: Point): Point {\n out ||= [0, 0]\n\n const {\n pos: [nodeX, nodeY],\n inputs,\n outputs\n } = this\n\n if (this.flags.collapsed) {\n const w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH\n out[0] = is_input ? nodeX : nodeX + w\n out[1] = nodeY - LiteGraph.NODE_TITLE_HEIGHT * 0.5\n return out\n }\n\n // weird feature that never got finished\n if (is_input && slot_number == -1) {\n out[0] = nodeX + LiteGraph.NODE_TITLE_HEIGHT * 0.5\n out[1] = nodeY + LiteGraph.NODE_TITLE_HEIGHT * 0.5\n return out\n }\n\n // hard-coded pos\n const inputPos = inputs?.[slot_number]?.pos\n const outputPos = outputs?.[slot_number]?.pos\n\n if (is_input && inputPos) {\n out[0] = nodeX + inputPos[0]\n out[1] = nodeY + inputPos[1]\n return out\n } else if (!is_input && outputPos) {\n out[0] = nodeX + outputPos[0]\n out[1] = nodeY + outputPos[1]\n return out\n }\n\n // default vertical slots\n const offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5\n const slotIndex = is_input\n ? this.#defaultVerticalInputs.indexOf(this.inputs[slot_number])\n : this.#defaultVerticalOutputs.indexOf(this.outputs[slot_number])\n\n out[0] = is_input ? nodeX + offset : nodeX + this.size[0] + 1 - offset\n out[1] =\n nodeY +\n (slotIndex + 0.7) * LiteGraph.NODE_SLOT_HEIGHT +\n (this.constructor.slot_start_y || 0)\n return out\n }\n\n /**\n * @internal The inputs that are not positioned with absolute coordinates.\n */\n get #defaultVerticalInputs() {\n return this.inputs.filter(\n (slot) => !slot.pos && !(this.widgets?.length && isWidgetInputSlot(slot))\n )\n }\n\n /**\n * @internal The outputs that are not positioned with absolute coordinates.\n */\n get #defaultVerticalOutputs() {\n return this.outputs.filter((slot: INodeOutputSlot) => !slot.pos)\n }\n\n /**\n * Get the context needed for slot position calculations\n * @internal\n */\n #getSlotPositionContext(): SlotPositionContext {\n return {\n nodeX: this.pos[0],\n nodeY: this.pos[1],\n nodeWidth: this.size[0],\n nodeHeight: this.size[1],\n collapsed: this.flags.collapsed ?? false,\n collapsedWidth: this._collapsed_width,\n slotStartY: this.constructor.slot_start_y,\n inputs: this.inputs,\n outputs: this.outputs,\n widgets: this.widgets\n }\n }\n\n /**\n * Gets the position of an input slot, in graph co-ordinates.\n *\n * This method is preferred over the legacy {@link getConnectionPos} method.\n * @param slot Input slot index\n * @returns Position of the input slot\n */\n getInputPos(slot: number): Point {\n return getSlotPosition(this, slot, true)\n }\n\n /**\n * Gets the position of an input slot, in graph co-ordinates.\n * @param input The actual node input object\n * @returns Position of the centre of the input slot in graph co-ordinates.\n */\n getInputSlotPos(input: INodeInputSlot): Point {\n return calculateInputSlotPosFromSlot(this.#getSlotPositionContext(), input)\n }\n\n /**\n * Gets the position of an output slot, in graph co-ordinates.\n *\n * This method is preferred over the legacy {@link getConnectionPos} method.\n * @param outputSlotIndex Output slot index\n * @returns Position of the output slot\n */\n getOutputPos(outputSlotIndex: number): Point {\n return getSlotPosition(this, outputSlotIndex, false)\n }\n\n /**\n * Get slot position using layout tree if available, fallback to node's position * Unified implementation used by both LitegraphLinkAdapter and useLinkLayoutSync\n * @param slotIndex The slot index\n * @param isInput Whether this is an input slot\n * @returns Position of the slot center in graph coordinates\n */\n getSlotPosition(slotIndex: number, isInput: boolean): Point {\n return getSlotPosition(this, slotIndex, isInput)\n }\n\n /** @inheritdoc */\n snapToGrid(snapTo: number): boolean {\n return this.pinned ? false : snapPoint(this.pos, snapTo)\n }\n\n /** @see {@link snapToGrid} */\n alignToGrid(): void {\n this.snapToGrid(LiteGraph.CANVAS_GRID_SIZE)\n }\n\n /* Console output */\n trace(msg: string): void {\n this.console ||= []\n this.console.push(msg)\n // @ts-expect-error deprecated\n if (this.console.length > LGraphNode.MAX_CONSOLE) this.console.shift()\n }\n\n /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */\n setDirtyCanvas(dirty_foreground: boolean, dirty_background?: boolean): void {\n this.graph?.canvasAction((c) =>\n c.setDirty(dirty_foreground, dirty_background)\n )\n }\n\n loadImage(url: string): HTMLImageElement {\n interface AsyncImageElement extends HTMLImageElement {\n ready?: boolean\n }\n\n const img: AsyncImageElement = new Image()\n img.src = LiteGraph.node_images_path + url\n img.ready = false\n\n const dirty = () => this.setDirtyCanvas(true)\n img.addEventListener('load', function (this: AsyncImageElement) {\n this.ready = true\n dirty()\n })\n return img\n }\n\n /**\n * Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus\n * @deprecated Use {@link LGraphCanvas.pointer} instead.\n */\n captureInput(v: boolean): void {\n warnDeprecated(\n '[DEPRECATED] captureInput will be removed in a future version. Please use LGraphCanvas.pointer (CanvasPointer) instead.'\n )\n if (!this.graph || !this.graph.list_of_graphcanvas) return\n\n const list = this.graph.list_of_graphcanvas\n\n for (const c of list) {\n // releasing somebody elses capture?!\n if (!v && c.node_capturing_input != this) continue\n\n // change\n c.node_capturing_input = v ? this : null\n }\n }\n\n get collapsed() {\n return !!this.flags.collapsed\n }\n\n get collapsible() {\n return !this.pinned && this.constructor.collapsable !== false\n }\n\n /**\n * Toggle node collapse (makes it smaller on the canvas)\n */\n collapse(force?: boolean): void {\n if (!this.collapsible && !force) return\n if (!this.graph) throw new NullGraphError()\n this.graph._version++\n this.flags.collapsed = !this.flags.collapsed\n this.setDirtyCanvas(true, true)\n }\n\n /**\n * Toggles advanced mode of the node, showing advanced widgets\n */\n toggleAdvanced() {\n if (!this.widgets?.some((w) => w.advanced)) return\n if (!this.graph) throw new NullGraphError()\n this.graph._version++\n this.showAdvanced = !this.showAdvanced\n this.expandToFitContent()\n this.setDirtyCanvas(true, true)\n }\n\n get pinned() {\n return !!this.flags.pinned\n }\n\n /**\n * Prevents the node being accidentally moved or resized by mouse interaction.\n * Toggles pinned state if no value is provided.\n */\n pin(v?: boolean): void {\n if (!this.graph) throw new NullGraphError()\n\n this.graph._version++\n this.flags.pinned = v ?? !this.flags.pinned\n this.resizable = !this.pinned\n if (!this.pinned) this.flags.pinned = undefined\n }\n\n unpin(): void {\n this.pin(false)\n }\n\n localToScreen(x: number, y: number, dragAndScale: DragAndScale): Point {\n return [\n (x + this.pos[0]) * dragAndScale.scale + dragAndScale.offset[0],\n (y + this.pos[1]) * dragAndScale.scale + dragAndScale.offset[1]\n ]\n }\n\n get width() {\n return this.collapsed\n ? this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH\n : this.size[0]\n }\n\n /**\n * Returns the height of the node, including the title bar.\n */\n get height() {\n return LiteGraph.NODE_TITLE_HEIGHT + this.bodyHeight\n }\n\n /**\n * Returns the height of the node, excluding the title bar.\n */\n get bodyHeight() {\n return this.collapsed ? 0 : this.size[1]\n }\n\n drawBadges(ctx: CanvasRenderingContext2D, { gap = 2 } = {}): void {\n const badgeInstances = this.badges.map((badge) =>\n badge instanceof LGraphBadge ? badge : badge()\n )\n const isLeftAligned = this.badgePosition === BadgePosition.TopLeft\n\n let currentX = isLeftAligned\n ? 0\n : this.width -\n badgeInstances.reduce(\n (acc, badge) => acc + badge.getWidth(ctx) + gap,\n 0\n )\n const y = -(LiteGraph.NODE_TITLE_HEIGHT + gap)\n\n for (const badge of badgeInstances) {\n badge.draw(ctx, currentX, y - badge.height)\n currentX += badge.getWidth(ctx) + gap\n }\n }\n\n /**\n * Renders the node's title bar background\n */\n drawTitleBarBackground(\n ctx: CanvasRenderingContext2D,\n {\n scale,\n title_height = LiteGraph.NODE_TITLE_HEIGHT,\n low_quality = false\n }: DrawTitleOptions\n ): void {\n const fgcolor = this.renderingColor\n const shape = this.renderingShape\n const size = this.renderingSize\n\n if (this.onDrawTitleBar) {\n this.onDrawTitleBar(ctx, title_height, size, scale, fgcolor)\n return\n }\n\n if (this.title_mode === TitleMode.TRANSPARENT_TITLE) {\n return\n }\n\n if (this.collapsed) {\n ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR\n }\n\n ctx.fillStyle = this.constructor.title_color || fgcolor\n ctx.beginPath()\n\n if (shape == RenderShape.BOX || low_quality) {\n ctx.rect(0, -title_height, size[0], title_height)\n } else if (shape == RenderShape.ROUND || shape == RenderShape.CARD) {\n ctx.roundRect(\n 0,\n -title_height,\n size[0],\n title_height,\n this.collapsed\n ? [LiteGraph.ROUND_RADIUS]\n : [LiteGraph.ROUND_RADIUS, LiteGraph.ROUND_RADIUS, 0, 0]\n )\n }\n ctx.fill()\n ctx.shadowColor = 'transparent'\n }\n\n /**\n * Renders the node's title box, i.e. the dot in front of the title text that\n * when clicked toggles the node's collapsed state. The term `title box` comes\n * from the original LiteGraph implementation.\n */\n drawTitleBox(\n ctx: CanvasRenderingContext2D,\n {\n scale,\n low_quality = false,\n title_height = LiteGraph.NODE_TITLE_HEIGHT,\n box_size = 10\n }: DrawTitleBoxOptions\n ): void {\n const size = this.renderingSize\n const shape = this.renderingShape\n\n if (this.onDrawTitleBox) {\n this.onDrawTitleBox(ctx, title_height, size, scale)\n return\n }\n\n if (\n [RenderShape.ROUND, RenderShape.CIRCLE, RenderShape.CARD].includes(shape)\n ) {\n if (low_quality) {\n ctx.fillStyle = 'black'\n ctx.beginPath()\n ctx.arc(\n title_height * 0.5,\n title_height * -0.5,\n box_size * 0.5 + 1,\n 0,\n Math.PI * 2\n )\n ctx.fill()\n }\n\n ctx.fillStyle = this.renderingBoxColor\n if (low_quality) {\n ctx.fillRect(\n title_height * 0.5 - box_size * 0.5,\n title_height * -0.5 - box_size * 0.5,\n box_size,\n box_size\n )\n } else {\n ctx.beginPath()\n ctx.arc(\n title_height * 0.5,\n title_height * -0.5,\n box_size * 0.5,\n 0,\n Math.PI * 2\n )\n ctx.fill()\n }\n } else {\n if (low_quality) {\n ctx.fillStyle = 'black'\n ctx.fillRect(\n (title_height - box_size) * 0.5 - 1,\n (title_height + box_size) * -0.5 - 1,\n box_size + 2,\n box_size + 2\n )\n }\n ctx.fillStyle = this.renderingBoxColor\n ctx.fillRect(\n (title_height - box_size) * 0.5,\n (title_height + box_size) * -0.5,\n box_size,\n box_size\n )\n }\n }\n\n /**\n * Renders the node's title text.\n */\n drawTitleText(\n ctx: CanvasRenderingContext2D,\n {\n scale,\n default_title_color,\n low_quality = false,\n title_height = LiteGraph.NODE_TITLE_HEIGHT\n }: DrawTitleTextOptions\n ): void {\n const size = this.renderingSize\n const selected = this.selected\n\n if (this.onDrawTitleText) {\n this.onDrawTitleText(\n ctx,\n title_height,\n size,\n scale,\n this.titleFontStyle,\n selected\n )\n return\n }\n\n // Don't render title text if low quality\n if (low_quality) {\n return\n }\n\n ctx.font = this.titleFontStyle\n const rawTitle = this.getTitle() ?? `❌ ${this.type}`\n const title = String(rawTitle) + (this.pinned ? '📌' : '')\n if (title) {\n if (selected) {\n ctx.fillStyle = LiteGraph.NODE_SELECTED_TITLE_COLOR\n } else {\n ctx.fillStyle = this.constructor.title_text_color || default_title_color\n }\n\n // Calculate available width for title\n let availableWidth = size[0] - title_height * 2 // Basic margins\n\n // Subtract space for title buttons\n if (this.title_buttons?.length > 0) {\n let buttonsWidth = 0\n const savedFont = ctx.font // Save current font\n for (const button of this.title_buttons) {\n if (button.visible) {\n buttonsWidth += button.getWidth(ctx) + 2 // button width + gap\n }\n }\n ctx.font = savedFont // Restore font after button measurements\n if (buttonsWidth > 0) {\n buttonsWidth -= 20 // Reduce by empty padding\n availableWidth -= buttonsWidth\n }\n }\n\n // Truncate title if needed\n let displayTitle = title\n\n if (this.collapsed) {\n // For collapsed nodes, limit to 20 chars as before\n displayTitle = title.substr(0, 20)\n } else if (availableWidth > 0) {\n // For regular nodes, truncate based on available width\n displayTitle = truncateText(ctx, title, availableWidth)\n }\n\n ctx.textAlign = 'left'\n ctx.fillText(\n displayTitle,\n title_height,\n LiteGraph.NODE_TITLE_TEXT_Y - title_height\n )\n }\n }\n\n /**\n * Attempts to gracefully bypass this node in all of its connections by reconnecting all links.\n *\n * Each input is checked against each output. This is done on a matching index basis, i.e. input 3 -> output 3.\n * If there are any input links remaining,\n * and {@link flags}.{@link INodeFlags.keepAllLinksOnBypass keepAllLinksOnBypass} is `true`,\n * each input will check for outputs that match, and take the first one that matches\n * `true`: Try the index matching first, then every input to every output.\n * `false`: Only matches indexes, e.g. input 3 to output 3.\n *\n * If {@link flags}.{@link INodeFlags.keepAllLinksOnBypass keepAllLinksOnBypass} is `undefined`, it will fall back to\n * the static {@link keepAllLinksOnBypass}.\n * @returns `true` if any new links were established, otherwise `false`.\n * @todo Decision: Change API to return array of new links instead?\n */\n connectInputToOutput(): boolean | undefined {\n const { inputs, outputs, graph } = this\n if (!inputs || !outputs) return\n if (!graph) throw new NullGraphError()\n\n const { _links } = graph\n let madeAnyConnections = false\n\n // First pass: only match exactly index-to-index\n for (const [index, input] of inputs.entries()) {\n if (input.link == null) continue\n\n const output = outputs[index]\n if (!output || !LiteGraph.isValidConnection(input.type, output.type))\n continue\n\n const inLink = _links.get(input.link)\n if (!inLink) continue\n const inNode = graph.getNodeById(inLink?.origin_id)\n if (!inNode) continue\n\n bypassAllLinks(output, inNode, inLink, graph)\n }\n // Configured to only use index-to-index matching\n if (!(this.flags.keepAllLinksOnBypass ?? LGraphNode.keepAllLinksOnBypass))\n return madeAnyConnections\n\n // Second pass: match any remaining links\n for (const input of inputs) {\n if (input.link == null) continue\n\n const inLink = _links.get(input.link)\n if (!inLink) continue\n const inNode = graph.getNodeById(inLink?.origin_id)\n if (!inNode) continue\n\n for (const output of outputs) {\n if (!LiteGraph.isValidConnection(input.type, output.type)) continue\n\n bypassAllLinks(output, inNode, inLink, graph)\n break\n }\n }\n return madeAnyConnections\n\n function bypassAllLinks(\n output: INodeOutputSlot,\n inNode: LGraphNode,\n inLink: LLink,\n graph: LGraph\n ) {\n const outLinks = output.links\n ?.map((x) => _links.get(x))\n .filter((x) => !!x)\n if (!outLinks?.length) return\n\n for (const outLink of outLinks) {\n const outNode = graph.getNodeById(outLink.target_id)\n if (!outNode) continue\n\n const result = inNode.connect(\n inLink.origin_slot,\n outNode,\n outLink.target_slot,\n inLink.parentId\n )\n madeAnyConnections ||= !!result\n }\n }\n }\n\n /**\n * Returns `true` if the widget is visible, otherwise `false`.\n */\n isWidgetVisible(widget: IBaseWidget): boolean {\n const isHidden =\n this.collapsed || widget.hidden || (widget.advanced && !this.showAdvanced)\n return !isHidden\n }\n\n updateComputedDisabled() {\n if (!this.widgets) return\n for (const widget of this.widgets)\n widget.computedDisabled =\n widget.disabled || this.getSlotFromWidget(widget)?.link != null\n }\n\n drawWidgets(\n ctx: CanvasRenderingContext2D,\n { lowQuality = false, editorAlpha = 1 }: DrawWidgetsOptions\n ): void {\n if (!this.widgets) return\n\n const nodeWidth = this.size[0]\n const { widgets } = this\n const H = LiteGraph.NODE_WIDGET_HEIGHT\n const showText = !lowQuality\n ctx.save()\n ctx.globalAlpha = editorAlpha\n\n this.updateComputedDisabled()\n for (const widget of widgets) {\n if (!this.isWidgetVisible(widget)) continue\n\n const { y } = widget\n const outlineColour = widget.advanced\n ? LiteGraph.WIDGET_ADVANCED_OUTLINE_COLOR\n : LiteGraph.WIDGET_OUTLINE_COLOR\n\n widget.last_y = y\n\n ctx.strokeStyle = outlineColour\n ctx.fillStyle = '#222'\n ctx.textAlign = 'left'\n if (widget.computedDisabled) ctx.globalAlpha *= 0.5\n const width = widget.width || nodeWidth\n\n if (typeof widget.draw === 'function') {\n widget.draw(ctx, this, width, y, H, lowQuality)\n } else {\n toConcreteWidget(widget, this, false)?.drawWidget(ctx, {\n width,\n showText\n })\n }\n ctx.globalAlpha = editorAlpha\n }\n ctx.restore()\n }\n\n /**\n * When {@link LGraphNode.collapsed} is `true`, this method draws the node's collapsed slots.\n */\n drawCollapsedSlots(ctx: CanvasRenderingContext2D): void {\n // Render the first connected slot only.\n for (const slot of this.#concreteInputs) {\n if (slot.link != null) {\n slot.drawCollapsed(ctx)\n break\n }\n }\n for (const slot of this.#concreteOutputs) {\n if (slot.links?.length) {\n slot.drawCollapsed(ctx)\n break\n }\n }\n }\n\n get slots(): (INodeInputSlot | INodeOutputSlot)[] {\n return [...this.inputs, ...this.outputs]\n }\n\n #measureSlot(\n slot: NodeInputSlot | NodeOutputSlot,\n slotIndex: number,\n isInput: boolean\n ): void {\n const pos = isInput\n ? this.getInputPos(slotIndex)\n : this.getOutputPos(slotIndex)\n\n slot.boundingRect[0] = pos[0] - LiteGraph.NODE_SLOT_HEIGHT * 0.5\n slot.boundingRect[1] = pos[1] - LiteGraph.NODE_SLOT_HEIGHT * 0.5\n slot.boundingRect[2] = slot.isWidgetInputSlot\n ? BaseWidget.margin\n : LiteGraph.NODE_SLOT_HEIGHT\n slot.boundingRect[3] = LiteGraph.NODE_SLOT_HEIGHT\n }\n\n #measureSlots(): ReadOnlyRect | null {\n const slots: (NodeInputSlot | NodeOutputSlot)[] = []\n\n for (const [slotIndex, slot] of this.#concreteInputs.entries()) {\n // Unrecognized nodes (Nodes with error) has inputs but no widgets. Treat\n // converted inputs as normal inputs.\n /** Widget input slots are handled in {@link layoutWidgetInputSlots} */\n if (this.widgets?.length && isWidgetInputSlot(slot)) continue\n\n this.#measureSlot(slot, slotIndex, true)\n slots.push(slot)\n }\n for (const [slotIndex, slot] of this.#concreteOutputs.entries()) {\n this.#measureSlot(slot, slotIndex, false)\n slots.push(slot)\n }\n\n return slots.length ? createBounds(slots, 0) : null\n }\n\n #getMouseOverSlot(slot: INodeSlot): INodeSlot | null {\n const isInput = isINodeInputSlot(slot)\n const mouseOverId = this.mouseOver?.[isInput ? 'inputId' : 'outputId'] ?? -1\n if (mouseOverId === -1) {\n return null\n }\n return isInput ? this.inputs[mouseOverId] : this.outputs[mouseOverId]\n }\n\n #isMouseOverSlot(slot: INodeSlot): boolean {\n return this.#getMouseOverSlot(slot) === slot\n }\n\n #isMouseOverWidget(widget: IBaseWidget | undefined): boolean {\n if (!widget) return false\n return this.mouseOver?.overWidget === widget\n }\n\n /**\n * Returns the input slot that is associated with the given widget.\n */\n getSlotFromWidget(\n widget: IBaseWidget | undefined\n ): INodeInputSlot | undefined {\n if (widget)\n return this.inputs.find(\n (slot) => isWidgetInputSlot(slot) && slot.widget.name === widget.name\n )\n }\n\n /**\n * Returns the widget that is associated with the given input slot.\n */\n getWidgetFromSlot(slot: INodeInputSlot): IBaseWidget | undefined {\n if (!isWidgetInputSlot(slot)) return\n return this.widgets?.find((w) => w.name === slot.widget.name)\n }\n\n /**\n * Draws the node's input and output slots.\n */\n drawSlots(\n ctx: CanvasRenderingContext2D,\n { fromSlot, colorContext, editorAlpha, lowQuality }: DrawSlotsOptions\n ) {\n for (const slot of [...this.#concreteInputs, ...this.#concreteOutputs]) {\n const isValidTarget = fromSlot && slot.isValidTarget(fromSlot)\n const isMouseOverSlot = this.#isMouseOverSlot(slot)\n\n // change opacity of incompatible slots when dragging a connection\n const isValid = !fromSlot || isValidTarget\n const highlight = isValid && isMouseOverSlot\n\n // Show slot if it's not a widget input slot\n // or if it's a widget input slot and satisfies one of the following:\n // - the mouse is over the widget\n // - the slot is valid during link drop\n // - the slot is connected\n if (\n isMouseOverSlot ||\n isValidTarget ||\n !slot.isWidgetInputSlot ||\n this.#isMouseOverWidget(this.getWidgetFromSlot(slot)) ||\n slot.isConnected ||\n slot.alwaysVisible\n ) {\n ctx.globalAlpha = isValid ? editorAlpha : 0.4 * editorAlpha\n slot.draw(ctx, {\n colorContext,\n lowQuality,\n highlight\n })\n }\n }\n }\n\n /**\n * Arranges the node's widgets vertically.\n * Sets following properties on each widget:\n * - {@link IBaseWidget.computedHeight}\n * - {@link IBaseWidget.y}\n * @param widgetStartY The y-coordinate of the first widget\n */\n #arrangeWidgets(widgetStartY: number): void {\n if (!this.widgets || !this.widgets.length) return\n\n const bodyHeight = this.bodyHeight\n const startY =\n this.widgets_start_y ?? (this.widgets_up ? 0 : widgetStartY) + 2\n\n let freeSpace = bodyHeight - startY\n\n // Collect fixed height widgets first\n let fixedWidgetHeight = 0\n const growableWidgets: {\n minHeight: number\n prefHeight?: number\n w: IBaseWidget\n }[] = []\n\n const visibleWidgets = this.widgets.filter((w) => !w.hidden)\n\n for (const w of visibleWidgets) {\n if (w.computeSize) {\n const height = w.computeSize()[1] + 4\n w.computedHeight = height\n fixedWidgetHeight += height\n } else if (w.computeLayoutSize) {\n const { minHeight, maxHeight } = w.computeLayoutSize(this)\n growableWidgets.push({\n minHeight,\n prefHeight: maxHeight,\n w\n })\n } else {\n const height = LiteGraph.NODE_WIDGET_HEIGHT + 4\n w.computedHeight = height\n fixedWidgetHeight += height\n }\n }\n\n // Calculate remaining space for DOM widgets\n freeSpace -= fixedWidgetHeight\n this.freeWidgetSpace = freeSpace\n\n // Prepare space requests for distribution\n const spaceRequests = growableWidgets.map((d) => ({\n minSize: d.minHeight,\n maxSize: d.prefHeight\n }))\n\n // Distribute space among DOM widgets\n const allocations = distributeSpace(Math.max(0, freeSpace), spaceRequests)\n\n // Apply computed heights\n for (const [i, d] of growableWidgets.entries()) {\n d.w.computedHeight = allocations[i]\n }\n\n // Position widgets\n let y = startY\n for (const w of visibleWidgets) {\n w.y = y\n y += w.computedHeight ?? 0\n }\n\n if (!this.graph) throw new NullGraphError()\n\n // Grow the node if necessary.\n // Ref: https://github.com/Comfy-Org/ComfyUI_frontend/issues/2652\n // TODO: Move the layout logic before drawing of the node shape, so we don't\n // need to trigger extra round of rendering.\n if (y > bodyHeight) {\n this.setSize([this.size[0], y])\n this.graph.setDirtyCanvas(false, true)\n }\n }\n\n /**\n * Arranges the layout of the node's widget input slots.\n */\n #arrangeWidgetInputSlots(): void {\n if (!this.widgets) return\n\n const slotByWidgetName = new Map<\n string,\n INodeInputSlot & { index: number }\n >()\n\n for (const [i, slot] of this.inputs.entries()) {\n if (!isWidgetInputSlot(slot)) continue\n\n slotByWidgetName.set(slot.widget.name, { ...slot, index: i })\n }\n if (!slotByWidgetName.size) return\n\n // Only set custom pos if not using Vue positioning\n // Vue positioning calculates widget slot positions dynamically\n if (!LiteGraph.vueNodesMode) {\n for (const widget of this.widgets) {\n const slot = slotByWidgetName.get(widget.name)\n if (!slot) continue\n\n const actualSlot = this.#concreteInputs[slot.index]\n const offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5\n actualSlot.pos = [offset, widget.y + offset]\n this.#measureSlot(actualSlot, slot.index, true)\n }\n } else {\n // For Vue positioning, just measure the slots without setting pos\n for (const widget of this.widgets) {\n const slot = slotByWidgetName.get(widget.name)\n if (!slot) continue\n\n this.#measureSlot(this.#concreteInputs[slot.index], slot.index, true)\n }\n }\n }\n\n /**\n * @internal Sets the internal concrete slot arrays, ensuring they are instances of\n * {@link NodeInputSlot} or {@link NodeOutputSlot}.\n *\n * A temporary workaround until duck-typed inputs and outputs\n * have been removed from the ecosystem.\n */\n _setConcreteSlots(): void {\n this.#concreteInputs = this.inputs.map((slot) =>\n toClass(NodeInputSlot, slot, this)\n )\n this.#concreteOutputs = this.outputs.map((slot) =>\n toClass(NodeOutputSlot, slot, this)\n )\n }\n\n /**\n * Arranges node elements in preparation for rendering (slots & widgets).\n */\n arrange(): void {\n const slotsBounds = this.#measureSlots()\n const widgetStartY = slotsBounds\n ? slotsBounds[1] + slotsBounds[3] - this.pos[1]\n : 0\n this.#arrangeWidgets(widgetStartY)\n this.#arrangeWidgetInputSlots()\n }\n\n /**\n * Draws a progress bar on the node.\n * @param ctx The canvas context to draw on\n */\n drawProgressBar(ctx: CanvasRenderingContext2D): void {\n if (!this.progress) return\n\n const originalFillStyle = ctx.fillStyle\n ctx.fillStyle = 'green'\n ctx.fillRect(0, 0, this.width * this.progress, 6)\n ctx.fillStyle = originalFillStyle\n }\n}\n","import { NullGraphError } from '@/lib/litegraph/src/infrastructure/NullGraphError'\n\nimport type { LGraph } from './LGraph'\nimport { LGraphCanvas } from './LGraphCanvas'\nimport { LGraphNode } from './LGraphNode'\nimport { strokeShape } from './draw'\nimport type {\n ColorOption,\n IColorable,\n IContextMenuValue,\n IPinnable,\n Point,\n Positionable,\n Size\n} from './interfaces'\nimport { LiteGraph, Rectangle } from './litegraph'\nimport {\n containsCentre,\n containsRect,\n createBounds,\n isInRectangle,\n isPointInRect,\n snapPoint\n} from './measure'\nimport type { ISerialisedGroup } from './types/serialisation'\n\nexport interface IGraphGroupFlags extends Record<string, unknown> {\n pinned?: true\n}\n\nexport class LGraphGroup implements Positionable, IPinnable, IColorable {\n static minWidth = 140\n static minHeight = 80\n static resizeLength = 10\n static padding = 4\n static defaultColour = '#335'\n\n id: number\n color?: string\n title: string\n font?: string\n font_size: number = LiteGraph.DEFAULT_GROUP_FONT || 24\n _bounding = new Rectangle(10, 10, LGraphGroup.minWidth, LGraphGroup.minHeight)\n\n _pos: Point = this._bounding.pos\n _size: Size = this._bounding.size\n /** @deprecated See {@link _children} */\n _nodes: LGraphNode[] = []\n _children: Set<Positionable> = new Set()\n graph?: LGraph\n flags: IGraphGroupFlags = {}\n selected?: boolean\n\n constructor(title?: string, id?: number) {\n // TODO: Object instantiation pattern requires too much boilerplate and null checking. ID should be passed in via constructor.\n this.id = id ?? -1\n this.title = title || 'Group'\n\n const { pale_blue } = LGraphCanvas.node_colors\n this.color = pale_blue ? pale_blue.groupcolor : '#AAA'\n }\n\n /** @inheritdoc {@link IColorable.setColorOption} */\n setColorOption(colorOption: ColorOption | null): void {\n if (colorOption == null) {\n delete this.color\n } else {\n this.color = colorOption.groupcolor\n }\n }\n\n /** @inheritdoc {@link IColorable.getColorOption} */\n getColorOption(): ColorOption | null {\n return (\n Object.values(LGraphCanvas.node_colors).find(\n (colorOption) => colorOption.groupcolor === this.color\n ) ?? null\n )\n }\n\n /** Position of the group, as x,y co-ordinates in graph space */\n get pos() {\n return this._pos\n }\n\n set pos(v) {\n if (!v || v.length < 2) return\n\n this._pos[0] = v[0]\n this._pos[1] = v[1]\n }\n\n /** Size of the group, as width,height in graph units */\n get size() {\n return this._size\n }\n\n set size(v) {\n if (!v || v.length < 2) return\n\n this._size[0] = Math.max(LGraphGroup.minWidth, v[0])\n this._size[1] = Math.max(LGraphGroup.minHeight, v[1])\n }\n\n get boundingRect() {\n return this._bounding\n }\n\n getBounding() {\n return this._bounding\n }\n\n get nodes() {\n return this._nodes\n }\n\n get titleHeight() {\n return this.font_size * 1.4\n }\n\n get children(): ReadonlySet<Positionable> {\n return this._children\n }\n\n get pinned() {\n return !!this.flags.pinned\n }\n\n /**\n * Prevents the group being accidentally moved or resized by mouse interaction.\n * Toggles pinned state if no value is provided.\n */\n pin(value?: boolean): void {\n const newState = value === undefined ? !this.pinned : value\n\n if (newState) this.flags.pinned = true\n else delete this.flags.pinned\n }\n\n unpin(): void {\n this.pin(false)\n }\n\n configure(o: ISerialisedGroup): void {\n this.id = o.id\n this.title = o.title\n this._bounding.set(o.bounding)\n this.color = o.color\n this.flags = o.flags || this.flags\n if (o.font_size) this.font_size = o.font_size\n }\n\n serialize(): ISerialisedGroup {\n const b = this._bounding\n return {\n id: this.id,\n title: this.title,\n bounding: [...b],\n color: this.color,\n font_size: this.font_size,\n flags: this.flags\n }\n }\n\n /**\n * Draws the group on the canvas\n * @param graphCanvas\n * @param ctx\n */\n draw(graphCanvas: LGraphCanvas, ctx: CanvasRenderingContext2D): void {\n const { padding, resizeLength, defaultColour } = LGraphGroup\n const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE\n\n const [x, y] = this._pos\n const [width, height] = this._size\n const color = this.color || defaultColour\n\n // Titlebar\n ctx.globalAlpha = 0.25 * graphCanvas.editor_alpha\n ctx.fillStyle = color\n ctx.strokeStyle = color\n ctx.beginPath()\n ctx.rect(x + 0.5, y + 0.5, width, font_size * 1.4)\n ctx.fill()\n\n // Group background, border\n ctx.fillStyle = color\n ctx.strokeStyle = color\n ctx.beginPath()\n ctx.rect(x + 0.5, y + 0.5, width, height)\n ctx.fill()\n ctx.globalAlpha = graphCanvas.editor_alpha\n ctx.stroke()\n\n // Resize marker\n ctx.beginPath()\n ctx.moveTo(x + width, y + height)\n ctx.lineTo(x + width - resizeLength, y + height)\n ctx.lineTo(x + width, y + height - resizeLength)\n ctx.fill()\n\n // Title\n ctx.font = `${font_size}px ${LiteGraph.GROUP_FONT}`\n ctx.textAlign = 'left'\n ctx.fillText(\n this.title + (this.pinned ? '📌' : ''),\n x + padding,\n y + font_size\n )\n\n if (LiteGraph.highlight_selected_group && this.selected) {\n strokeShape(ctx, this._bounding, {\n title_height: this.titleHeight,\n padding\n })\n }\n }\n\n resize(width: number, height: number): boolean {\n if (this.pinned) return false\n\n this._size[0] = Math.max(LGraphGroup.minWidth, width)\n this._size[1] = Math.max(LGraphGroup.minHeight, height)\n return true\n }\n\n move(deltaX: number, deltaY: number, skipChildren: boolean = false): void {\n if (this.pinned) return\n\n this._pos[0] += deltaX\n this._pos[1] += deltaY\n if (skipChildren === true) return\n\n for (const item of this._children) {\n item.move(deltaX, deltaY)\n }\n }\n\n /** @inheritdoc */\n snapToGrid(snapTo: number): boolean {\n return this.pinned ? false : snapPoint(this.pos, snapTo)\n }\n\n recomputeInsideNodes(): void {\n if (!this.graph) throw new NullGraphError()\n const { nodes, reroutes, groups } = this.graph\n const children = this._children\n this._nodes.length = 0\n children.clear()\n\n // Move nodes we overlap the centre point of\n for (const node of nodes) {\n if (containsCentre(this._bounding, node.boundingRect)) {\n this._nodes.push(node)\n children.add(node)\n }\n }\n\n // Move reroutes we overlap the centre point of\n for (const reroute of reroutes.values()) {\n if (isPointInRect(reroute.pos, this._bounding)) children.add(reroute)\n }\n\n // Move groups we wholly contain\n for (const group of groups) {\n if (containsRect(this._bounding, group._bounding)) children.add(group)\n }\n\n groups.sort((a, b) => {\n if (a === this) {\n return children.has(b) ? -1 : 0\n } else if (b === this) {\n return children.has(a) ? 1 : 0\n } else {\n return 0\n }\n })\n }\n\n /**\n * Resizes and moves the group to neatly fit all given {@link objects}.\n * @param objects All objects that should be inside the group\n * @param padding Value in graph units to add to all sides of the group. Default: 10\n */\n resizeTo(objects: Iterable<Positionable>, padding: number = 10): void {\n const boundingBox = createBounds(objects, padding)\n if (boundingBox === null) return\n\n this.pos[0] = boundingBox[0]\n this.pos[1] = boundingBox[1] - this.titleHeight\n this.size[0] = boundingBox[2]\n this.size[1] = boundingBox[3] + this.titleHeight\n }\n\n /**\n * Add nodes to the group and adjust the group's position and size accordingly\n * @param nodes The nodes to add to the group\n * @param padding The padding around the group\n */\n addNodes(nodes: LGraphNode[], padding: number = 10): void {\n if (!this._nodes && nodes.length === 0) return\n this.resizeTo([...this.children, ...this._nodes, ...nodes], padding)\n }\n\n getMenuOptions(): (\n | IContextMenuValue<string>\n | IContextMenuValue<string | null>\n | null\n )[] {\n return [\n {\n content: this.pinned ? 'Unpin' : 'Pin',\n callback: () => {\n if (this.pinned) this.unpin()\n else this.pin()\n this.setDirtyCanvas(false, true)\n }\n },\n null,\n { content: 'Title', callback: LGraphCanvas.onShowPropertyEditor },\n {\n content: 'Color',\n has_submenu: true,\n callback: LGraphCanvas.onMenuNodeColors\n },\n {\n content: 'Font size',\n property: 'font_size',\n type: 'Number',\n callback: LGraphCanvas.onShowPropertyEditor\n },\n null,\n { content: 'Remove', callback: LGraphCanvas.onMenuNodeRemove }\n ]\n }\n\n isPointInTitlebar(x: number, y: number): boolean {\n const b = this.boundingRect\n return isInRectangle(x, y, b[0], b[1], b[2], this.titleHeight)\n }\n\n isInResize(x: number, y: number): boolean {\n const b = this.boundingRect\n const right = b[0] + b[2]\n const bottom = b[1] + b[3]\n\n return (\n x < right &&\n y < bottom &&\n x - right + (y - bottom) > -LGraphGroup.resizeLength\n )\n }\n\n isPointInside = LGraphNode.prototype.isPointInside\n setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas\n}\n","import type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport {\n SUBGRAPH_INPUT_ID,\n SUBGRAPH_OUTPUT_ID\n} from '@/lib/litegraph/src/constants'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\n\nimport type { RenderLink } from './RenderLink'\n\n/**\n * Represents a floating link that is currently being dragged from one slot to another.\n *\n * This is a heavier, but short-lived convenience data structure. All refs to FloatingRenderLinks should be discarded on drop.\n * @remarks\n * At time of writing, Litegraph is using several different styles and methods to handle link dragging.\n *\n * Once the library has undergone more substantial changes to the way links are managed,\n * many properties of this class will be superfluous and removable.\n */\nexport class FloatingRenderLink implements RenderLink {\n readonly node: LGraphNode\n readonly fromSlot: INodeOutputSlot | INodeInputSlot\n readonly fromPos: Point\n readonly fromDirection: LinkDirection\n readonly fromSlotIndex: number\n\n readonly outputNodeId: NodeId = -1\n readonly outputNode?: LGraphNode\n readonly outputSlot?: INodeOutputSlot\n readonly outputIndex: number = -1\n readonly outputPos?: Point\n\n readonly inputNodeId: NodeId = -1\n readonly inputNode?: LGraphNode\n readonly inputSlot?: INodeInputSlot\n readonly inputIndex: number = -1\n readonly inputPos?: Point\n\n constructor(\n readonly network: LinkNetwork,\n readonly link: LLink,\n readonly toType: 'input' | 'output',\n readonly fromReroute: Reroute,\n readonly dragDirection: LinkDirection = LinkDirection.CENTER\n ) {\n const {\n origin_id: outputNodeId,\n target_id: inputNodeId,\n origin_slot: outputIndex,\n target_slot: inputIndex\n } = link\n\n if (outputNodeId !== -1) {\n // Output connected\n const outputNode = network.getNodeById(outputNodeId) ?? undefined\n if (!outputNode)\n throw new Error(\n `Creating DraggingRenderLink for link [${link.id}] failed: Output node [${outputNodeId}] not found.`\n )\n\n const outputSlot = outputNode?.outputs.at(outputIndex)\n if (!outputSlot)\n throw new Error(\n `Creating DraggingRenderLink for link [${link.id}] failed: Output slot [${outputIndex}] not found.`\n )\n\n this.outputNodeId = outputNodeId\n this.outputNode = outputNode\n this.outputSlot = outputSlot\n this.outputIndex = outputIndex\n this.outputPos = outputNode.getOutputPos(outputIndex)\n\n // RenderLink props\n this.node = outputNode\n this.fromSlot = outputSlot\n this.fromPos = fromReroute?.pos ?? this.outputPos\n this.fromDirection = LinkDirection.LEFT\n this.dragDirection = LinkDirection.RIGHT\n this.fromSlotIndex = outputIndex\n } else {\n // Input connected\n const inputNode = network.getNodeById(inputNodeId) ?? undefined\n if (!inputNode)\n throw new Error(\n `Creating DraggingRenderLink for link [${link.id}] failed: Input node [${inputNodeId}] not found.`\n )\n\n const inputSlot = inputNode?.inputs.at(inputIndex)\n if (!inputSlot)\n throw new Error(\n `Creating DraggingRenderLink for link [${link.id}] failed: Input slot [${inputIndex}] not found.`\n )\n\n this.inputNodeId = inputNodeId\n this.inputNode = inputNode\n this.inputSlot = inputSlot\n this.inputIndex = inputIndex\n this.inputPos = inputNode.getInputPos(inputIndex)\n\n // RenderLink props\n this.node = inputNode\n this.fromSlot = inputSlot\n this.fromDirection = LinkDirection.RIGHT\n this.fromSlotIndex = inputIndex\n }\n this.fromPos = fromReroute.pos\n }\n\n canConnectToInput(): boolean {\n return this.toType === 'input'\n }\n\n canConnectToOutput(): boolean {\n return this.toType === 'output'\n }\n\n canConnectToReroute(reroute: Reroute): boolean {\n if (this.toType === 'input') {\n if (reroute.origin_id === this.inputNode?.id) return false\n } else {\n if (reroute.origin_id === this.outputNode?.id) return false\n }\n return true\n }\n\n canConnectToSubgraphInput(input: SubgraphInput): boolean {\n return this.toType === 'output' && input.isValidTarget(this.fromSlot)\n }\n\n connectToInput(\n node: LGraphNode,\n input: INodeInputSlot,\n _events?: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const floatingLink = this.link\n floatingLink.target_id = node.id\n floatingLink.target_slot = node.inputs.indexOf(input)\n\n node.disconnectInput(node.inputs.indexOf(input))\n\n this.fromSlot._floatingLinks?.delete(floatingLink)\n input._floatingLinks ??= new Set()\n input._floatingLinks.add(floatingLink)\n }\n\n connectToOutput(\n node: LGraphNode,\n output: INodeOutputSlot,\n _events?: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const floatingLink = this.link\n floatingLink.origin_id = node.id\n floatingLink.origin_slot = node.outputs.indexOf(output)\n\n this.fromSlot._floatingLinks?.delete(floatingLink)\n output._floatingLinks ??= new Set()\n output._floatingLinks.add(floatingLink)\n }\n\n connectToSubgraphInput(\n input: SubgraphInput,\n _events?: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const floatingLink = this.link\n floatingLink.origin_id = SUBGRAPH_INPUT_ID\n floatingLink.origin_slot = input.parent.slots.indexOf(input)\n\n this.fromSlot._floatingLinks?.delete(floatingLink)\n input._floatingLinks ??= new Set()\n input._floatingLinks.add(floatingLink)\n }\n\n connectToSubgraphOutput(\n output: SubgraphOutput,\n _events?: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const floatingLink = this.link\n floatingLink.origin_id = SUBGRAPH_OUTPUT_ID\n floatingLink.origin_slot = output.parent.slots.indexOf(output)\n\n this.fromSlot._floatingLinks?.delete(floatingLink)\n output._floatingLinks ??= new Set()\n output._floatingLinks.add(floatingLink)\n }\n\n connectToRerouteInput(\n // @ts-expect-error - Reroute type needs fixing\n reroute: Reroute,\n { node: inputNode, input }: { node: LGraphNode; input: INodeInputSlot },\n events: CustomEventTarget<LinkConnectorEventMap>\n ) {\n const floatingLink = this.link\n floatingLink.target_id = inputNode.id\n floatingLink.target_slot = inputNode.inputs.indexOf(input)\n\n this.fromSlot._floatingLinks?.delete(floatingLink)\n input._floatingLinks ??= new Set()\n input._floatingLinks.add(floatingLink)\n\n events.dispatch('input-moved', this)\n }\n\n connectToRerouteOutput(\n // @ts-expect-error - Reroute type needs fixing\n reroute: Reroute,\n outputNode: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ) {\n const floatingLink = this.link\n floatingLink.origin_id = outputNode.id\n floatingLink.origin_slot = outputNode.outputs.indexOf(output)\n\n this.fromSlot._floatingLinks?.delete(floatingLink)\n output._floatingLinks ??= new Set()\n output._floatingLinks.add(floatingLink)\n\n events.dispatch('output-moved', this)\n }\n}\n","import type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\n\nimport type { RenderLink } from './RenderLink'\n\n/**\n * Represents an existing link that is currently being dragged by the user from one slot to another.\n *\n * This is a heavier, but short-lived convenience data structure.\n * All refs to {@link MovingInputLink} and {@link MovingOutputLink} should be discarded on drop.\n * @remarks\n * At time of writing, Litegraph is using several different styles and methods to handle link dragging.\n *\n * Once the library has undergone more substantial changes to the way links are managed,\n * many properties of this class will be superfluous and removable.\n */\n\nexport abstract class MovingLinkBase implements RenderLink {\n abstract readonly node: LGraphNode\n abstract readonly fromSlot: INodeOutputSlot | INodeInputSlot\n abstract readonly fromPos: Point\n abstract readonly fromDirection: LinkDirection\n abstract readonly fromSlotIndex: number\n\n readonly outputNodeId: NodeId\n readonly outputNode: LGraphNode\n readonly outputSlot: INodeOutputSlot\n readonly outputIndex: number\n readonly outputPos: Point\n\n readonly inputNodeId: NodeId\n readonly inputNode: LGraphNode\n readonly inputSlot: INodeInputSlot\n readonly inputIndex: number\n readonly inputPos: Point\n\n constructor(\n readonly network: LinkNetwork,\n readonly link: LLink,\n readonly toType: 'input' | 'output',\n readonly fromReroute?: Reroute,\n readonly dragDirection: LinkDirection = LinkDirection.CENTER\n ) {\n const {\n origin_id: outputNodeId,\n target_id: inputNodeId,\n origin_slot: outputIndex,\n target_slot: inputIndex\n } = link\n\n // Store output info\n const outputNode = network.getNodeById(outputNodeId) ?? undefined\n if (!outputNode)\n throw new Error(\n `Creating MovingRenderLink for link [${link.id}] failed: Output node [${outputNodeId}] not found.`\n )\n\n const outputSlot = outputNode.outputs.at(outputIndex)\n if (!outputSlot)\n throw new Error(\n `Creating MovingRenderLink for link [${link.id}] failed: Output slot [${outputIndex}] not found.`\n )\n\n this.outputNodeId = outputNodeId\n this.outputNode = outputNode\n this.outputSlot = outputSlot\n this.outputIndex = outputIndex\n this.outputPos = outputNode.getOutputPos(outputIndex)\n\n // Store input info\n const inputNode = network.getNodeById(inputNodeId) ?? undefined\n if (!inputNode)\n throw new Error(\n `Creating DraggingRenderLink for link [${link.id}] failed: Input node [${inputNodeId}] not found.`\n )\n\n const inputSlot = inputNode.inputs.at(inputIndex)\n if (!inputSlot)\n throw new Error(\n `Creating DraggingRenderLink for link [${link.id}] failed: Input slot [${inputIndex}] not found.`\n )\n\n this.inputNodeId = inputNodeId\n this.inputNode = inputNode\n this.inputSlot = inputSlot\n this.inputIndex = inputIndex\n this.inputPos = inputNode.getInputPos(inputIndex)\n }\n\n abstract canConnectToInput(\n inputNode: NodeLike,\n input: INodeInputSlot\n ): boolean\n abstract canConnectToOutput(\n outputNode: NodeLike,\n output: INodeOutputSlot\n ): boolean\n abstract connectToInput(\n node: LGraphNode,\n input: INodeInputSlot,\n events?: CustomEventTarget<LinkConnectorEventMap>\n ): void\n abstract connectToOutput(\n node: LGraphNode,\n output: INodeOutputSlot,\n events?: CustomEventTarget<LinkConnectorEventMap>\n ): void\n abstract connectToSubgraphInput(\n input: SubgraphInput,\n events?: CustomEventTarget<LinkConnectorEventMap>\n ): void\n abstract connectToSubgraphOutput(\n output: SubgraphOutput,\n events?: CustomEventTarget<LinkConnectorEventMap>\n ): void\n abstract connectToRerouteInput(\n reroute: Reroute,\n {\n node,\n input,\n link\n }: { node: LGraphNode; input: INodeInputSlot; link: LLink },\n events: CustomEventTarget<LinkConnectorEventMap>,\n originalReroutes: Reroute[]\n ): void\n abstract connectToRerouteOutput(\n reroute: Reroute,\n outputNode: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ): void\n\n abstract disconnect(): boolean\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\nimport type { SubgraphIO } from '@/lib/litegraph/src/types/serialisation'\n\nimport { MovingLinkBase } from './MovingLinkBase'\n\nexport class MovingInputLink extends MovingLinkBase {\n override readonly toType = 'input'\n\n readonly node: LGraphNode\n readonly fromSlot: INodeOutputSlot\n readonly fromPos: Point\n readonly fromDirection: LinkDirection\n readonly fromSlotIndex: number\n\n constructor(\n network: LinkNetwork,\n link: LLink,\n fromReroute?: Reroute,\n dragDirection: LinkDirection = LinkDirection.CENTER\n ) {\n super(network, link, 'input', fromReroute, dragDirection)\n\n this.node = this.outputNode\n this.fromSlot = this.outputSlot\n this.fromPos = fromReroute?.pos ?? this.outputPos\n this.fromDirection = LinkDirection.NONE\n this.fromSlotIndex = this.outputIndex\n }\n\n canConnectToInput(\n inputNode: NodeLike,\n input: INodeInputSlot | SubgraphIO\n ): boolean {\n return this.node.canConnectTo(inputNode, input, this.outputSlot)\n }\n\n canConnectToOutput(): false {\n return false\n }\n\n canConnectToReroute(reroute: Reroute): boolean {\n return reroute.origin_id !== this.inputNode.id\n }\n\n connectToInput(\n inputNode: LGraphNode,\n input: INodeInputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ): LLink | null | undefined {\n if (input === this.inputSlot) return\n\n this.inputNode.disconnectInput(this.inputIndex, true)\n const link = this.outputNode.connectSlots(\n this.outputSlot,\n inputNode,\n input,\n this.fromReroute?.id\n )\n if (link) events.dispatch('input-moved', this)\n return link\n }\n\n connectToOutput(): never {\n throw new Error('MovingInputLink cannot connect to an output.')\n }\n\n connectToSubgraphInput(): void {\n throw new Error('MovingInputLink cannot connect to a subgraph input.')\n }\n\n connectToSubgraphOutput(\n output: SubgraphOutput,\n events?: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const newLink = output.connect(\n this.fromSlot,\n this.node,\n this.fromReroute?.id\n )\n events?.dispatch('link-created', newLink)\n }\n\n connectToRerouteInput(\n reroute: Reroute,\n {\n node: inputNode,\n input,\n link: existingLink\n }: { node: LGraphNode; input: INodeInputSlot; link: LLink },\n events: CustomEventTarget<LinkConnectorEventMap>,\n originalReroutes: Reroute[]\n ): void {\n const { outputNode, outputSlot, fromReroute } = this\n\n // Clean up reroutes\n for (const reroute of originalReroutes) {\n if (reroute.id === this.link.parentId) break\n\n if (reroute.totalLinks === 1) reroute.remove()\n }\n // Set the parentId of the reroute we dropped on, to the reroute we dragged from\n reroute.parentId = fromReroute?.id\n\n const newLink = outputNode.connectSlots(\n outputSlot,\n inputNode,\n input,\n existingLink.parentId\n )\n if (newLink) events.dispatch('input-moved', this)\n }\n\n connectToRerouteOutput(): never {\n throw new Error('MovingInputLink cannot connect to an output.')\n }\n\n disconnect(): boolean {\n return this.inputNode.disconnectInput(this.inputIndex, true)\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\nimport type { SubgraphIO } from '@/lib/litegraph/src/types/serialisation'\n\nimport { MovingLinkBase } from './MovingLinkBase'\n\nexport class MovingOutputLink extends MovingLinkBase {\n override readonly toType = 'output'\n\n readonly node: LGraphNode\n readonly fromSlot: INodeInputSlot\n readonly fromPos: Point\n readonly fromDirection: LinkDirection\n readonly fromSlotIndex: number\n\n constructor(\n network: LinkNetwork,\n link: LLink,\n fromReroute?: Reroute,\n dragDirection: LinkDirection = LinkDirection.CENTER\n ) {\n super(network, link, 'output', fromReroute, dragDirection)\n\n this.node = this.inputNode\n this.fromSlot = this.inputSlot\n this.fromPos = fromReroute?.pos ?? this.inputPos\n this.fromDirection = LinkDirection.LEFT\n this.fromSlotIndex = this.inputIndex\n }\n\n canConnectToInput(): false {\n return false\n }\n\n canConnectToOutput(\n outputNode: NodeLike,\n output: INodeOutputSlot | SubgraphIO\n ): boolean {\n return outputNode.canConnectTo(this.node, this.inputSlot, output)\n }\n\n canConnectToReroute(reroute: Reroute): boolean {\n return reroute.origin_id !== this.outputNode.id\n }\n\n canConnectToSubgraphInput(input: SubgraphInput): boolean {\n return input.isValidTarget(this.fromSlot)\n }\n\n connectToInput(): never {\n throw new Error('MovingOutputLink cannot connect to an input.')\n }\n\n connectToOutput(\n outputNode: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ): LLink | null | undefined {\n if (output === this.outputSlot) return\n\n const link = outputNode.connectSlots(\n output,\n this.inputNode,\n this.inputSlot,\n this.link.parentId\n )\n if (link) events.dispatch('output-moved', this)\n return link\n }\n\n connectToSubgraphInput(\n input: SubgraphInput,\n events?: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const newLink = input.connect(\n this.fromSlot,\n this.node,\n this.fromReroute?.id\n )\n events?.dispatch('link-created', newLink)\n }\n\n connectToSubgraphOutput(): void {\n throw new Error('MovingOutputLink cannot connect to a subgraph output.')\n }\n\n connectToRerouteInput(): never {\n throw new Error('MovingOutputLink cannot connect to an input.')\n }\n\n connectToRerouteOutput(\n reroute: Reroute,\n outputNode: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n // Moving output side of links\n const { inputNode, inputSlot, fromReroute } = this\n\n // Creating a new link removes floating prop - check before connecting\n const floatingTerminus = reroute?.floating?.slotType === 'output'\n\n // Connect the first reroute of the link being dragged to the reroute being dropped on\n if (fromReroute) {\n fromReroute.parentId = reroute.id\n } else {\n // If there are no reroutes, directly connect the link\n this.link.parentId = reroute.id\n }\n // Use the last reroute id on the link to retain all reroutes\n outputNode.connectSlots(output, inputNode, inputSlot, this.link.parentId)\n\n // Connecting from the final reroute of a floating reroute chain\n if (floatingTerminus) reroute.removeAllFloatingLinks()\n\n events.dispatch('output-moved', this)\n }\n\n disconnect(): boolean {\n return this.outputNode.disconnectOutput(this.outputIndex, this.inputNode)\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeInputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { SubgraphInputNode } from '@/lib/litegraph/src/subgraph/SubgraphInputNode'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\n\nimport type { RenderLink } from './RenderLink'\n\n/** Connecting TO an input slot. */\n\nexport class ToInputFromIoNodeLink implements RenderLink {\n readonly toType = 'input'\n readonly fromSlotIndex: number\n readonly fromPos: Point\n fromDirection: LinkDirection = LinkDirection.RIGHT\n readonly existingLink?: LLink\n\n constructor(\n readonly network: LinkNetwork,\n readonly node: SubgraphInputNode,\n readonly fromSlot: SubgraphInput,\n readonly fromReroute?: Reroute,\n public dragDirection: LinkDirection = LinkDirection.CENTER,\n existingLink?: LLink\n ) {\n const outputIndex = node.slots.indexOf(fromSlot)\n if (outputIndex === -1 && fromSlot !== node.emptySlot) {\n throw new Error(\n `Creating render link for node [${this.node.id}] failed: Slot index not found.`\n )\n }\n\n this.fromSlotIndex = outputIndex\n this.fromPos = fromReroute ? fromReroute.pos : fromSlot.pos\n this.existingLink = existingLink\n }\n\n canConnectToInput(inputNode: NodeLike, input: INodeInputSlot): boolean {\n return this.node.canConnectTo(inputNode, input, this.fromSlot)\n }\n\n canConnectToOutput(): false {\n return false\n }\n\n connectToInput(\n node: LGraphNode,\n input: INodeInputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ) {\n const { fromSlot, fromReroute, existingLink } = this\n\n const newLink = fromSlot.connect(input, node, fromReroute?.id)\n\n if (existingLink) {\n // Moving an existing link\n const { input, inputNode } = existingLink.resolve(this.network)\n if (inputNode && input)\n this.node._disconnectNodeInput(inputNode, input, existingLink)\n events.dispatch('input-moved', this)\n } else {\n // Creating a new link\n events.dispatch('link-created', newLink)\n }\n }\n\n connectToSubgraphOutput(): void {\n throw new Error('Not implemented')\n }\n\n connectToRerouteInput(\n reroute: Reroute,\n {\n node: inputNode,\n input,\n link\n }: { node: LGraphNode; input: INodeInputSlot; link: LLink },\n events: CustomEventTarget<LinkConnectorEventMap>,\n originalReroutes: Reroute[]\n ) {\n const { fromSlot, fromReroute } = this\n\n // Check before creating new link overwrites the value\n const floatingTerminus = fromReroute?.floating?.slotType === 'output'\n\n // Set the parentId of the reroute we dropped on, to the reroute we dragged from\n reroute.parentId = fromReroute?.id\n\n const newLink = fromSlot.connect(input, inputNode, link.parentId)\n\n // Connecting from the final reroute of a floating reroute chain\n if (floatingTerminus) fromReroute.removeAllFloatingLinks()\n\n // Clean up reroutes\n for (const reroute of originalReroutes) {\n if (reroute.id === fromReroute?.id) break\n\n reroute.removeLink(link)\n if (reroute.totalLinks === 0) {\n if (link.isFloating) {\n // Cannot float from both sides - remove\n reroute.remove()\n } else {\n // Convert to floating\n const cl = link.toFloating('output', reroute.id)\n this.network.addFloatingLink(cl)\n reroute.floating = { slotType: 'output' }\n }\n }\n }\n\n if (this.existingLink) {\n // Moving an existing link\n events.dispatch('input-moved', this)\n } else {\n // Creating a new link\n events.dispatch('link-created', newLink)\n }\n }\n\n connectToOutput() {\n throw new Error('ToInputRenderLink cannot connect to an output.')\n }\n\n connectToSubgraphInput(): void {\n throw new Error('ToInputRenderLink cannot connect to a subgraph input.')\n }\n\n connectToRerouteOutput() {\n throw new Error('ToInputRenderLink cannot connect to an output.')\n }\n disconnect(): boolean {\n if (!this.existingLink) return false\n const { input, inputNode } = this.existingLink.resolve(this.network)\n if (!inputNode || !input) return false\n this.node._disconnectNodeInput(inputNode, input, this.existingLink)\n return true\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\n\nimport type { RenderLink } from './RenderLink'\n\n/** Connecting TO an input slot. */\n\nexport class ToInputRenderLink implements RenderLink {\n readonly toType = 'input'\n readonly fromPos: Point\n readonly fromSlotIndex: number\n fromDirection: LinkDirection = LinkDirection.RIGHT\n\n constructor(\n readonly network: LinkNetwork,\n readonly node: LGraphNode,\n readonly fromSlot: INodeOutputSlot,\n readonly fromReroute?: Reroute,\n public dragDirection: LinkDirection = LinkDirection.CENTER\n ) {\n const outputIndex = node.outputs.indexOf(fromSlot)\n if (outputIndex === -1)\n throw new Error(\n `Creating render link for node [${this.node.id}] failed: Slot index not found.`\n )\n\n this.fromSlotIndex = outputIndex\n this.fromPos = fromReroute\n ? fromReroute.pos\n : this.node.getOutputPos(outputIndex)\n }\n\n canConnectToInput(inputNode: NodeLike, input: INodeInputSlot): boolean {\n return this.node.canConnectTo(inputNode, input, this.fromSlot)\n }\n\n canConnectToOutput(): false {\n return false\n }\n\n connectToInput(\n node: LGraphNode,\n input: INodeInputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ) {\n const { node: outputNode, fromSlot, fromReroute } = this\n if (node === outputNode) return\n\n const newLink = outputNode.connectSlots(\n fromSlot,\n node,\n input,\n fromReroute?.id\n )\n events.dispatch('link-created', newLink)\n }\n\n connectToSubgraphOutput(\n output: SubgraphOutput,\n events: CustomEventTarget<LinkConnectorEventMap>\n ) {\n const newLink = output.connect(\n this.fromSlot,\n this.node,\n this.fromReroute?.id\n )\n events.dispatch('link-created', newLink)\n }\n\n connectToRerouteInput(\n reroute: Reroute,\n {\n node: inputNode,\n input,\n link\n }: { node: LGraphNode; input: INodeInputSlot; link: LLink },\n events: CustomEventTarget<LinkConnectorEventMap>,\n originalReroutes: Reroute[]\n ) {\n const { node: outputNode, fromSlot, fromReroute } = this\n\n // Check before creating new link overwrites the value\n const floatingTerminus = fromReroute?.floating?.slotType === 'output'\n\n // Set the parentId of the reroute we dropped on, to the reroute we dragged from\n reroute.parentId = fromReroute?.id\n\n const newLink = outputNode.connectSlots(\n fromSlot,\n inputNode,\n input,\n link.parentId\n )\n\n // Connecting from the final reroute of a floating reroute chain\n if (floatingTerminus) fromReroute.removeAllFloatingLinks()\n\n // Clean up reroutes\n for (const reroute of originalReroutes) {\n if (reroute.id === fromReroute?.id) break\n\n reroute.removeLink(link)\n if (reroute.totalLinks === 0) {\n if (link.isFloating) {\n // Cannot float from both sides - remove\n reroute.remove()\n } else {\n // Convert to floating\n const cl = link.toFloating('output', reroute.id)\n this.network.addFloatingLink(cl)\n reroute.floating = { slotType: 'output' }\n }\n }\n }\n events.dispatch('link-created', newLink)\n }\n\n connectToOutput() {\n throw new Error('ToInputRenderLink cannot connect to an output.')\n }\n\n connectToSubgraphInput(): void {\n throw new Error('ToInputRenderLink cannot connect to a subgraph input.')\n }\n\n connectToRerouteOutput() {\n throw new Error('ToInputRenderLink cannot connect to an output.')\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeOutputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport type { SubgraphOutputNode } from '@/lib/litegraph/src/subgraph/SubgraphOutputNode'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\nimport type { SubgraphIO } from '@/lib/litegraph/src/types/serialisation'\n\nimport type { RenderLink } from './RenderLink'\n\n/** Connecting TO an output slot. */\n\nexport class ToOutputFromIoNodeLink implements RenderLink {\n readonly toType = 'output'\n readonly fromPos: Point\n readonly fromSlotIndex: number\n fromDirection: LinkDirection = LinkDirection.LEFT\n\n constructor(\n readonly network: LinkNetwork,\n readonly node: SubgraphOutputNode,\n readonly fromSlot: SubgraphOutput,\n readonly fromReroute?: Reroute,\n public dragDirection: LinkDirection = LinkDirection.CENTER\n ) {\n const inputIndex = node.slots.indexOf(fromSlot)\n if (inputIndex === -1 && fromSlot !== node.emptySlot) {\n throw new Error(\n `Creating render link for node [${this.node.id}] failed: Slot index not found.`\n )\n }\n\n this.fromSlotIndex = inputIndex\n this.fromPos = fromReroute ? fromReroute.pos : fromSlot.pos\n }\n\n canConnectToInput(): false {\n return false\n }\n\n canConnectToOutput(\n outputNode: NodeLike,\n output: INodeOutputSlot | SubgraphIO\n ): boolean {\n return this.node.canConnectTo(outputNode, this.fromSlot, output)\n }\n\n canConnectToReroute(reroute: Reroute): boolean {\n if (reroute.origin_id === this.node.id) return false\n return true\n }\n\n connectToOutput(\n node: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ) {\n const { fromSlot, fromReroute } = this\n\n const newLink = fromSlot.connect(output, node, fromReroute?.id)\n events.dispatch('link-created', newLink)\n }\n\n connectToSubgraphInput(): void {\n throw new Error('Not implemented')\n }\n\n connectToRerouteOutput(\n reroute: Reroute,\n outputNode: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const { fromSlot } = this\n\n const newLink = fromSlot.connect(output, outputNode, reroute?.id)\n events.dispatch('link-created', newLink)\n }\n\n connectToInput() {\n throw new Error('ToOutputRenderLink cannot connect to an input.')\n }\n\n connectToSubgraphOutput(): void {\n throw new Error('ToOutputRenderLink cannot connect to a subgraph output.')\n }\n\n connectToRerouteInput() {\n throw new Error('ToOutputRenderLink cannot connect to an input.')\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n Point\n} from '@/lib/litegraph/src/interfaces'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport type { NodeLike } from '@/lib/litegraph/src/types/NodeLike'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\nimport type { SubgraphIO } from '@/lib/litegraph/src/types/serialisation'\n\nimport type { RenderLink } from './RenderLink'\n\n/** Connecting TO an output slot. */\n\nexport class ToOutputRenderLink implements RenderLink {\n readonly toType = 'output'\n readonly fromPos: Point\n readonly fromSlotIndex: number\n fromDirection: LinkDirection = LinkDirection.LEFT\n\n constructor(\n readonly network: LinkNetwork,\n readonly node: LGraphNode,\n readonly fromSlot: INodeInputSlot,\n readonly fromReroute?: Reroute,\n public dragDirection: LinkDirection = LinkDirection.CENTER\n ) {\n const inputIndex = node.inputs.indexOf(fromSlot)\n if (inputIndex === -1)\n throw new Error(\n `Creating render link for node [${this.node.id}] failed: Slot index not found.`\n )\n\n this.fromSlotIndex = inputIndex\n this.fromPos = fromReroute\n ? fromReroute.pos\n : this.node.getInputPos(inputIndex)\n }\n\n canConnectToInput(): false {\n return false\n }\n\n canConnectToOutput(\n outputNode: NodeLike,\n output: INodeOutputSlot | SubgraphIO\n ): boolean {\n return this.node.canConnectTo(outputNode, this.fromSlot, output)\n }\n\n canConnectToReroute(reroute: Reroute): boolean {\n if (reroute.origin_id === this.node.id) return false\n return true\n }\n\n canConnectToSubgraphInput(input: SubgraphInput): boolean {\n return input.isValidTarget(this.fromSlot)\n }\n\n connectToOutput(\n node: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ) {\n const { node: inputNode, fromSlot, fromReroute } = this\n if (!inputNode) return\n\n const newLink = node.connectSlots(\n output,\n inputNode,\n fromSlot,\n fromReroute?.id\n )\n events.dispatch('link-created', newLink)\n }\n\n connectToSubgraphInput(\n input: SubgraphInput,\n events?: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const newLink = input.connect(\n this.fromSlot,\n this.node,\n this.fromReroute?.id\n )\n events?.dispatch('link-created', newLink)\n }\n\n connectToRerouteOutput(\n reroute: Reroute,\n outputNode: LGraphNode,\n output: INodeOutputSlot,\n events: CustomEventTarget<LinkConnectorEventMap>\n ): void {\n const { node: inputNode, fromSlot } = this\n const newLink = outputNode.connectSlots(\n output,\n inputNode,\n fromSlot,\n reroute?.id\n )\n events.dispatch('link-created', newLink)\n }\n\n connectToInput() {\n throw new Error('ToOutputRenderLink cannot connect to an input.')\n }\n\n connectToSubgraphOutput(): void {\n throw new Error('ToOutputRenderLink cannot connect to a subgraph output.')\n }\n\n connectToRerouteInput() {\n throw new Error('ToOutputRenderLink cannot connect to an input.')\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport type {\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork\n} from '@/lib/litegraph/src/litegraph'\n\nimport type { LinkConnector } from './LinkConnector'\nimport { ToInputRenderLink } from './ToInputRenderLink'\nimport { ToOutputRenderLink } from './ToOutputRenderLink'\n\n/**\n * @internal A workaround class to support connecting to reroutes to node outputs.\n */\nexport class ToOutputFromRerouteLink extends ToOutputRenderLink {\n constructor(\n network: LinkNetwork,\n node: LGraphNode,\n fromSlot: INodeInputSlot,\n override readonly fromReroute: Reroute,\n readonly linkConnector: LinkConnector\n ) {\n super(network, node, fromSlot, fromReroute)\n }\n\n override canConnectToReroute(): false {\n return false\n }\n\n override connectToOutput(node: LGraphNode, output: INodeOutputSlot) {\n const nuRenderLink = new ToInputRenderLink(this.network, node, output)\n this.linkConnector._connectOutputToReroute(this.fromReroute, nuRenderLink)\n }\n}\n","import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { LLink } from '@/lib/litegraph/src/LLink'\nimport type { Reroute } from '@/lib/litegraph/src/Reroute'\nimport {\n SUBGRAPH_INPUT_ID,\n SUBGRAPH_OUTPUT_ID\n} from '@/lib/litegraph/src/constants'\nimport { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'\nimport type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'\nimport type {\n ConnectingLink,\n INodeInputSlot,\n INodeOutputSlot,\n ItemLocator,\n LinkNetwork,\n LinkSegment\n} from '@/lib/litegraph/src/interfaces'\nimport { EmptySubgraphInput } from '@/lib/litegraph/src/subgraph/EmptySubgraphInput'\nimport { EmptySubgraphOutput } from '@/lib/litegraph/src/subgraph/EmptySubgraphOutput'\nimport { Subgraph } from '@/lib/litegraph/src/subgraph/Subgraph'\nimport type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'\nimport { SubgraphInputNode } from '@/lib/litegraph/src/subgraph/SubgraphInputNode'\nimport type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'\nimport { SubgraphOutputNode } from '@/lib/litegraph/src/subgraph/SubgraphOutputNode'\nimport type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'\nimport { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'\nimport type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'\n\nimport { FloatingRenderLink } from './FloatingRenderLink'\nimport { MovingInputLink } from './MovingInputLink'\nimport { MovingLinkBase } from './MovingLinkBase'\nimport { MovingOutputLink } from './MovingOutputLink'\nimport type { RenderLink } from './RenderLink'\nimport { ToInputFromIoNodeLink } from './ToInputFromIoNodeLink'\nimport { ToInputRenderLink } from './ToInputRenderLink'\nimport { ToOutputFromIoNodeLink } from './ToOutputFromIoNodeLink'\nimport { ToOutputFromRerouteLink } from './ToOutputFromRerouteLink'\nimport { ToOutputRenderLink } from './ToOutputRenderLink'\n\n/**\n * A Litegraph state object for the {@link LinkConnector}.\n * References are only held atomically within a function, never passed.\n * The concrete implementation may be replaced or proxied without side-effects.\n */\ninterface LinkConnectorState {\n /**\n * The type of slot that links are being connected **to**.\n * - When `undefined`, no operation is being performed.\n * - A change in this property indicates the start or end of dragging links.\n */\n connectingTo: 'input' | 'output' | undefined\n multi: boolean\n /** When `true`, existing links are being repositioned. Otherwise, new links are being created. */\n draggingExistingLinks: boolean\n /** When set, connecting links will all snap to this position. */\n snapLinksPos?: [number, number]\n}\n\n/** Discriminated union to simplify type narrowing. */\ntype RenderLinkUnion =\n | MovingInputLink\n | MovingOutputLink\n | FloatingRenderLink\n | ToInputRenderLink\n | ToOutputRenderLink\n | ToInputFromIoNodeLink\n | ToOutputFromIoNodeLink\n\ninterface LinkConnectorExport {\n renderLinks: RenderLink[]\n inputLinks: LLink[]\n outputLinks: LLink[]\n floatingLinks: LLink[]\n state: LinkConnectorState\n network: LinkNetwork\n}\n\n/**\n * Component of {@link LGraphCanvas} that handles connecting and moving links.\n * @see {@link LLink}\n */\nexport class LinkConnector {\n /**\n * Link connection state POJO. Source of truth for state of link drag operations.\n *\n * Can be replaced or proxied to allow notifications.\n * Is always dereferenced at the start of an operation.\n */\n state: LinkConnectorState = {\n connectingTo: undefined,\n multi: false,\n draggingExistingLinks: false,\n snapLinksPos: undefined\n }\n\n readonly events = new CustomEventTarget<LinkConnectorEventMap>()\n\n /** Contains information for rendering purposes only. */\n readonly renderLinks: RenderLinkUnion[] = []\n\n /** Existing links that are being moved **to** a new input slot. */\n readonly inputLinks: LLink[] = []\n /** Existing links that are being moved **to** a new output slot. */\n readonly outputLinks: LLink[] = []\n /** Existing floating links that are being moved to a new slot. */\n readonly floatingLinks: LLink[] = []\n\n readonly hiddenReroutes: Set<Reroute> = new Set()\n\n /** The widget beneath the pointer, if it is a valid connection target. */\n overWidget?: IBaseWidget\n /** The type (returned by downstream callback) for {@link overWidget} */\n overWidgetType?: string\n\n /** The reroute beneath the pointer, if it is a valid connection target. */\n overReroute?: Reroute\n\n readonly #setConnectingLinks: (value: ConnectingLink[]) => void\n\n constructor(setConnectingLinks: (value: ConnectingLink[]) => void) {\n this.#setConnectingLinks = setConnectingLinks\n }\n\n get isConnecting() {\n return this.state.connectingTo !== undefined\n }\n\n get draggingExistingLinks() {\n return this.state.draggingExistingLinks\n }\n\n /** Drag an existing link to a different input. */\n moveInputLink(network: LinkNetwork, input: INodeInputSlot): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const { state, inputLinks, renderLinks } = this\n\n const linkId = input.link\n if (linkId == null) {\n // No link connected, check for a floating link\n const floatingLink = input._floatingLinks?.values().next().value\n if (floatingLink?.parentId == null) return\n\n try {\n const reroute = network.reroutes.get(floatingLink.parentId)\n if (!reroute)\n throw new Error(\n `Invalid reroute id: [${floatingLink.parentId}] for floating link id: [${floatingLink.id}].`\n )\n\n const renderLink = new FloatingRenderLink(\n network,\n floatingLink,\n 'input',\n reroute\n )\n const mayContinue = this.events.dispatch(\n 'before-move-input',\n renderLink\n )\n if (mayContinue === false) return\n\n renderLinks.push(renderLink)\n } catch (error) {\n console.warn(\n `Could not create render link for link id: [${floatingLink.id}].`,\n floatingLink,\n error\n )\n }\n\n floatingLink._dragging = true\n this.floatingLinks.push(floatingLink)\n } else {\n const link = network.links.get(linkId)\n if (!link) return\n\n // Special handling for links from subgraph input nodes\n if (link.origin_id === SUBGRAPH_INPUT_ID) {\n // For subgraph input links, we need to handle them differently\n // since they don't have a regular output node\n const subgraphInput = network.inputNode?.slots[link.origin_slot]\n if (!subgraphInput) {\n console.warn(\n `Could not find subgraph input for slot [${link.origin_slot}]`\n )\n return\n }\n\n try {\n const reroute = network.getReroute(link.parentId)\n const renderLink = new ToInputFromIoNodeLink(\n network,\n network.inputNode,\n subgraphInput,\n reroute,\n LinkDirection.CENTER,\n link\n )\n\n // Note: We don't dispatch the before-move-input event for subgraph input links\n // as the event type doesn't support ToInputFromIoNodeLink\n\n renderLinks.push(renderLink)\n\n this.listenUntilReset('input-moved', () => {\n link.disconnect(network, 'input')\n })\n } catch (error) {\n console.warn(\n `Could not create render link for subgraph input link id: [${link.id}].`,\n link,\n error\n )\n return\n }\n\n link._dragging = true\n inputLinks.push(link)\n } else {\n // Regular node links\n try {\n const reroute = network.getReroute(link.parentId)\n const renderLink = new MovingInputLink(network, link, reroute)\n\n const mayContinue = this.events.dispatch(\n 'before-move-input',\n renderLink\n )\n if (mayContinue === false) return\n\n renderLinks.push(renderLink)\n\n this.listenUntilReset('input-moved', (e) => {\n if ('link' in e.detail && e.detail.link) {\n e.detail.link.disconnect(network, 'output')\n }\n })\n } catch (error) {\n console.warn(\n `Could not create render link for link id: [${link.id}].`,\n link,\n error\n )\n return\n }\n\n link._dragging = true\n inputLinks.push(link)\n }\n }\n\n state.connectingTo = 'input'\n state.draggingExistingLinks = true\n\n this.#setLegacyLinks(false)\n }\n\n /** Drag all links from an output to a new output. */\n moveOutputLink(network: LinkNetwork, output: INodeOutputSlot): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const { state, renderLinks } = this\n\n // Floating links\n if (output._floatingLinks?.size) {\n for (const floatingLink of output._floatingLinks.values()) {\n try {\n const reroute = LLink.getFirstReroute(network, floatingLink)\n if (!reroute)\n throw new Error(\n `Invalid reroute id: [${floatingLink.parentId}] for floating link id: [${floatingLink.id}].`\n )\n\n const renderLink = new FloatingRenderLink(\n network,\n floatingLink,\n 'output',\n reroute\n )\n const mayContinue = this.events.dispatch(\n 'before-move-output',\n renderLink\n )\n if (mayContinue === false) continue\n\n renderLinks.push(renderLink)\n this.floatingLinks.push(floatingLink)\n } catch (error) {\n console.warn(\n `Could not create render link for link id: [${floatingLink.id}].`,\n floatingLink,\n error\n )\n }\n }\n }\n\n // Normal links\n if (output.links?.length) {\n for (const linkId of output.links) {\n const link = network.links.get(linkId)\n if (!link) continue\n\n const firstReroute = LLink.getFirstReroute(network, link)\n if (firstReroute) {\n firstReroute._dragging = true\n this.hiddenReroutes.add(firstReroute)\n } else {\n link._dragging = true\n }\n this.outputLinks.push(link)\n\n try {\n if (link.target_id === SUBGRAPH_OUTPUT_ID) {\n if (!(network instanceof Subgraph)) {\n console.warn(\n 'Subgraph output link found in non-subgraph network.'\n )\n continue\n }\n\n const output = network.outputs.at(link.target_slot)\n if (!output) throw new Error('No subgraph output found for link.')\n\n const renderLink = new ToOutputFromIoNodeLink(\n network,\n network.outputNode,\n output\n )\n renderLink.fromDirection = LinkDirection.NONE\n renderLinks.push(renderLink)\n\n continue\n }\n const renderLink = new MovingOutputLink(\n network,\n link,\n firstReroute,\n LinkDirection.RIGHT\n )\n\n const mayContinue = this.events.dispatch(\n 'before-move-output',\n renderLink\n )\n if (mayContinue === false) continue\n\n renderLinks.push(renderLink)\n } catch (error) {\n console.warn(\n `Could not create render link for link id: [${link.id}].`,\n link,\n error\n )\n continue\n }\n }\n }\n\n if (renderLinks.length === 0) return\n\n state.draggingExistingLinks = true\n state.multi = true\n state.connectingTo = 'output'\n\n this.#setLegacyLinks(true)\n }\n\n /**\n * Drags a new link from an output slot to an input slot.\n * @param network The network that the link being connected belongs to\n * @param node The node the link is being dragged from\n * @param output The output slot that the link is being dragged from\n */\n dragNewFromOutput(\n network: LinkNetwork,\n node: LGraphNode,\n output: INodeOutputSlot,\n fromReroute?: Reroute\n ): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const { state } = this\n const renderLink = new ToInputRenderLink(network, node, output, fromReroute)\n this.renderLinks.push(renderLink)\n\n state.connectingTo = 'input'\n\n this.#setLegacyLinks(false)\n }\n\n /**\n * Drags a new link from an input slot to an output slot.\n * @param network The network that the link being connected belongs to\n * @param node The node the link is being dragged from\n * @param input The input slot that the link is being dragged from\n */\n dragNewFromInput(\n network: LinkNetwork,\n node: LGraphNode,\n input: INodeInputSlot,\n fromReroute?: Reroute\n ): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const { state } = this\n const renderLink = new ToOutputRenderLink(network, node, input, fromReroute)\n this.renderLinks.push(renderLink)\n\n state.connectingTo = 'output'\n\n this.#setLegacyLinks(true)\n }\n\n dragNewFromSubgraphInput(\n network: LinkNetwork,\n inputNode: SubgraphInputNode,\n input: SubgraphInput,\n fromReroute?: Reroute\n ): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const renderLink = new ToInputFromIoNodeLink(\n network,\n inputNode,\n input,\n fromReroute\n )\n this.renderLinks.push(renderLink)\n\n this.state.connectingTo = 'input'\n\n this.#setLegacyLinks(false)\n }\n\n dragNewFromSubgraphOutput(\n network: LinkNetwork,\n outputNode: SubgraphOutputNode,\n output: SubgraphOutput,\n fromReroute?: Reroute\n ): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const renderLink = new ToOutputFromIoNodeLink(\n network,\n outputNode,\n output,\n fromReroute\n )\n this.renderLinks.push(renderLink)\n\n this.state.connectingTo = 'output'\n\n this.#setLegacyLinks(true)\n }\n\n /**\n * Drags a new link from a reroute to an input slot.\n * @param network The network that the link being connected belongs to\n * @param reroute The reroute that the link is being dragged from\n */\n dragFromReroute(network: LinkNetwork, reroute: Reroute): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const link = reroute.firstLink ?? reroute.firstFloatingLink\n if (!link) {\n console.warn('No link found for reroute.')\n return\n }\n\n if (link.origin_id === SUBGRAPH_INPUT_ID) {\n if (!(network instanceof Subgraph)) {\n console.warn('Subgraph input link found in non-subgraph network.')\n return\n }\n\n const input = network.inputs.at(link.origin_slot)\n if (!input) throw new Error('No subgraph input found for link.')\n\n const renderLink = new ToInputFromIoNodeLink(\n network,\n network.inputNode,\n input,\n reroute\n )\n renderLink.fromDirection = LinkDirection.NONE\n this.renderLinks.push(renderLink)\n\n this.state.connectingTo = 'input'\n\n this.#setLegacyLinks(false)\n return\n }\n\n const outputNode = network.getNodeById(link.origin_id)\n if (!outputNode) {\n console.warn('No output node found for link.', link)\n return\n }\n\n const outputSlot = outputNode.outputs.at(link.origin_slot)\n if (!outputSlot) {\n console.warn('No output slot found for link.', link)\n return\n }\n\n const renderLink = new ToInputRenderLink(\n network,\n outputNode,\n outputSlot,\n reroute\n )\n renderLink.fromDirection = LinkDirection.NONE\n this.renderLinks.push(renderLink)\n\n this.state.connectingTo = 'input'\n\n this.#setLegacyLinks(false)\n }\n\n /**\n * Drags a new link from a reroute to an output slot.\n * @param network The network that the link being connected belongs to\n * @param reroute The reroute that the link is being dragged from\n */\n dragFromRerouteToOutput(network: LinkNetwork, reroute: Reroute): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const link = reroute.firstLink ?? reroute.firstFloatingLink\n if (!link) {\n console.warn('No link found for reroute.')\n return\n }\n\n if (link.target_id === SUBGRAPH_OUTPUT_ID) {\n if (!(network instanceof Subgraph)) {\n console.warn('Subgraph output link found in non-subgraph network.')\n return\n }\n\n const output = network.outputs.at(link.target_slot)\n if (!output) throw new Error('No subgraph output found for link.')\n\n const renderLink = new ToOutputFromIoNodeLink(\n network,\n network.outputNode,\n output,\n reroute\n )\n renderLink.fromDirection = LinkDirection.NONE\n this.renderLinks.push(renderLink)\n\n this.state.connectingTo = 'output'\n\n this.#setLegacyLinks(false)\n return\n }\n\n const inputNode = network.getNodeById(link.target_id)\n if (!inputNode) {\n console.warn('No input node found for link.', link)\n return\n }\n\n const inputSlot = inputNode.inputs.at(link.target_slot)\n if (!inputSlot) {\n console.warn('No input slot found for link.', link)\n return\n }\n\n const renderLink = new ToOutputFromRerouteLink(\n network,\n inputNode,\n inputSlot,\n reroute,\n this\n )\n renderLink.fromDirection = LinkDirection.LEFT\n this.renderLinks.push(renderLink)\n\n this.state.connectingTo = 'output'\n\n this.#setLegacyLinks(true)\n }\n\n dragFromLinkSegment(network: LinkNetwork, linkSegment: LinkSegment): void {\n if (this.isConnecting) throw new Error('Already dragging links.')\n\n const { state } = this\n if (linkSegment.origin_id == null || linkSegment.origin_slot == null) return\n\n const node = network.getNodeById(linkSegment.origin_id)\n if (!node) return\n\n const slot = node.outputs.at(linkSegment.origin_slot)\n if (!slot) return\n\n const reroute = network.getReroute(linkSegment.parentId)\n const renderLink = new ToInputRenderLink(network, node, slot, reroute)\n renderLink.fromDirection = LinkDirection.NONE\n this.renderLinks.push(renderLink)\n\n state.connectingTo = 'input'\n\n this.#setLegacyLinks(false)\n }\n\n /**\n * Connects the links being dropped\n * @param event Contains the drop location, in canvas space\n */\n dropLinks(locator: ItemLocator, event: CanvasPointerEvent): void {\n if (!this.isConnecting) {\n const mayContinue = this.events.dispatch('before-drop-links', {\n renderLinks: this.renderLinks,\n event\n })\n if (mayContinue === false) return\n }\n\n try {\n const { canvasX, canvasY } = event\n\n const ioNode = locator.getIoNodeOnPos?.(canvasX, canvasY)\n if (ioNode) {\n this.dropOnIoNode(ioNode, event)\n return\n }\n\n const node = locator.getNodeOnPos(canvasX, canvasY) ?? undefined\n if (node) {\n this.dropOnNode(node, event)\n } else {\n // Get reroute if no node is found\n const reroute = locator.getRerouteOnPos(canvasX, canvasY)\n // Drop output->input link on reroute is not impl.\n if (reroute && this.isRerouteValidDrop(reroute)) {\n this.dropOnReroute(reroute, event)\n } else {\n this.dropOnNothing(event)\n }\n }\n } finally {\n this.events.dispatch('after-drop-links', {\n renderLinks: this.renderLinks,\n event\n })\n }\n }\n\n dropOnIoNode(\n ioNode: SubgraphInputNode | SubgraphOutputNode,\n event: CanvasPointerEvent\n ): void {\n const { renderLinks, state } = this\n const { connectingTo } = state\n const { canvasX, canvasY } = event\n\n if (connectingTo === 'input' && ioNode instanceof SubgraphOutputNode) {\n const output = ioNode.getSlotInPosition(canvasX, canvasY)\n if (!output) {\n this.dropOnNothing(event)\n return\n }\n\n // Track the actual slot to use for all connections\n let targetSlot = output\n\n for (const link of renderLinks) {\n link.connectToSubgraphOutput(targetSlot, this.events)\n\n // If we just connected to an EmptySubgraphOutput, check if we should reuse the slot\n if (output instanceof EmptySubgraphOutput && ioNode.slots.length > 0) {\n // Get the last created slot (newest one)\n const createdSlot = ioNode.slots[ioNode.slots.length - 1]\n\n // Only reuse the slot if the next link's type would be compatible\n // Otherwise, keep using EmptySubgraphOutput to create a new slot\n const nextLink = renderLinks[renderLinks.indexOf(link) + 1]\n if (nextLink && link.fromSlot.type === nextLink.fromSlot.type) {\n targetSlot = createdSlot\n } else {\n // Reset to EmptySubgraphOutput for different types\n targetSlot = output\n }\n }\n }\n } else if (\n connectingTo === 'output' &&\n ioNode instanceof SubgraphInputNode\n ) {\n const input = ioNode.getSlotInPosition(canvasX, canvasY)\n if (!input) {\n this.dropOnNothing(event)\n return\n }\n\n // Same logic for SubgraphInputNode if needed\n let targetSlot = input\n\n for (const link of renderLinks) {\n // Validate the connection type before proceeding\n if (\n 'canConnectToSubgraphInput' in link &&\n !link.canConnectToSubgraphInput(targetSlot)\n ) {\n console.warn(\n 'Invalid connection type',\n link.fromSlot.type,\n '->',\n targetSlot.type\n )\n continue\n }\n\n link.connectToSubgraphInput(targetSlot, this.events)\n\n // If we just connected to an EmptySubgraphInput, check if we should reuse the slot\n if (input instanceof EmptySubgraphInput && ioNode.slots.length > 0) {\n // Get the last created slot (newest one)\n const createdSlot = ioNode.slots[ioNode.slots.length - 1]\n\n // Only reuse the slot if the next link's type would be compatible\n // Otherwise, keep using EmptySubgraphInput to create a new slot\n const nextLink = renderLinks[renderLinks.indexOf(link) + 1]\n if (nextLink && link.fromSlot.type === nextLink.fromSlot.type) {\n targetSlot = createdSlot\n } else {\n // Reset to EmptySubgraphInput for different types\n targetSlot = input\n }\n }\n }\n } else {\n console.error(\n 'Invalid connectingTo state &/ ioNode',\n connectingTo,\n ioNode\n )\n }\n }\n\n dropOnNode(node: LGraphNode, event: CanvasPointerEvent) {\n const { renderLinks, state } = this\n const { connectingTo } = state\n const { canvasX, canvasY } = event\n\n // Do nothing if every connection would loop back\n if (renderLinks.every((link) => link.node === node)) return\n\n // To output\n if (connectingTo === 'output') {\n const output = node.getOutputOnPos([canvasX, canvasY])\n\n if (output) {\n this.#dropOnOutput(node, output)\n } else {\n this.connectToNode(node, event)\n }\n // To input\n } else if (connectingTo === 'input') {\n const input = node.getInputOnPos([canvasX, canvasY])\n const inputOrSocket = input ?? node.getSlotFromWidget(this.overWidget)\n\n // Input slot\n if (inputOrSocket) {\n this.#dropOnInput(node, inputOrSocket)\n } else {\n // Node background / title\n this.connectToNode(node, event)\n }\n }\n }\n\n dropOnReroute(reroute: Reroute, event: CanvasPointerEvent): void {\n const mayContinue = this.events.dispatch('dropped-on-reroute', {\n reroute,\n event\n })\n if (mayContinue === false) return\n\n // Connecting to input\n if (this.state.connectingTo === 'input') {\n if (this.renderLinks.length !== 1)\n throw new Error(\n `Attempted to connect ${this.renderLinks.length} input links to a reroute.`\n )\n\n const renderLink = this.renderLinks[0]\n this._connectOutputToReroute(reroute, renderLink)\n\n return\n }\n\n // Connecting to output\n for (const link of this.renderLinks) {\n if (link.toType !== 'output') continue\n\n const result = reroute.findSourceOutput()\n if (!result) continue\n\n const { node, output } = result\n if (!link.canConnectToOutput(node, output)) continue\n\n link.connectToRerouteOutput(reroute, node, output, this.events)\n }\n }\n\n /** @internal Temporary workaround - requires refactor. */\n _connectOutputToReroute(reroute: Reroute, renderLink: RenderLinkUnion): void {\n const results = reroute.findTargetInputs()\n if (!results?.length) return\n\n const maybeReroutes = reroute.getReroutes()\n if (maybeReroutes === null) throw new Error('Reroute loop detected.')\n\n const originalReroutes = maybeReroutes.slice(0, -1).reverse()\n\n // From reroute to reroute\n if (renderLink instanceof ToInputRenderLink) {\n const { node, fromSlot, fromSlotIndex, fromReroute } = renderLink\n\n reroute.setFloatingLinkOrigin(node, fromSlot, fromSlotIndex)\n\n // Clean floating link IDs from reroutes about to be removed from the chain\n if (fromReroute != null) {\n for (const originalReroute of originalReroutes) {\n if (originalReroute.id === fromReroute.id) break\n\n for (const linkId of reroute.floatingLinkIds) {\n originalReroute.floatingLinkIds.delete(linkId)\n }\n }\n }\n }\n\n // Filter before any connections are re-created\n const filtered = results.filter(\n (result) =>\n renderLink.toType === 'input' &&\n canConnectInputLinkToReroute(\n renderLink,\n result.node,\n result.input,\n reroute\n )\n )\n\n for (const result of filtered) {\n renderLink.connectToRerouteInput(\n reroute,\n result,\n this.events,\n originalReroutes\n )\n }\n\n return\n }\n\n dropOnNothing(event: CanvasPointerEvent): void {\n // For external event only.\n const mayContinue = this.events.dispatch('dropped-on-canvas', event)\n if (mayContinue === false) return\n\n this.disconnectLinks()\n }\n\n /**\n * Disconnects all moving links.\n * @remarks This is called when the links are dropped on the canvas.\n * May be called by consumers to e.g. drag links into a bin / void.\n */\n disconnectLinks(): void {\n for (const link of this.renderLinks) {\n if (\n link instanceof MovingLinkBase ||\n link instanceof ToInputFromIoNodeLink\n ) {\n link.disconnect()\n }\n }\n }\n\n /**\n * Connects the links being dropped onto a node to the first matching slot.\n * @param node The node that the links are being dropped on\n * @param event Contains the drop location, in canvas space\n */\n connectToNode(node: LGraphNode, event: CanvasPointerEvent): void {\n const {\n state: { connectingTo }\n } = this\n\n const mayContinue = this.events.dispatch('dropped-on-node', { node, event })\n if (mayContinue === false) return\n\n // Assume all links are the same type, disallow loopback\n const firstLink = this.renderLinks[0]\n if (!firstLink) return\n\n // Use a single type check before looping; ensures all dropped links go to the same slot\n if (connectingTo === 'output') {\n // Dropping new output link\n const output = node.findOutputByType(firstLink.fromSlot.type)?.slot\n if (output === undefined) {\n console.warn(\n `Could not find slot for link type: [${firstLink.fromSlot.type}].`\n )\n return\n }\n\n this.#dropOnOutput(node, output)\n } else if (connectingTo === 'input') {\n // Dropping new input link\n const input = node.findInputByType(firstLink.fromSlot.type)?.slot\n if (input === undefined) {\n console.warn(\n `Could not find slot for link type: [${firstLink.fromSlot.type}].`\n )\n return\n }\n\n this.#dropOnInput(node, input)\n }\n }\n\n #dropOnInput(node: LGraphNode, input: INodeInputSlot): void {\n for (const link of this.renderLinks) {\n if (!link.canConnectToInput(node, input)) continue\n\n link.connectToInput(node, input, this.events)\n }\n }\n\n #dropOnOutput(node: LGraphNode, output: INodeOutputSlot): void {\n for (const link of this.renderLinks) {\n if (!link.canConnectToOutput(node, output)) {\n if (\n link instanceof MovingOutputLink &&\n link.link.parentId !== undefined\n ) {\n // Reconnect link without reroutes\n link.outputNode.connectSlots(\n link.outputSlot,\n link.inputNode,\n link.inputSlot,\n undefined!\n )\n }\n continue\n }\n\n link.connectToOutput(node, output, this.events)\n }\n }\n\n isInputValidDrop(node: LGraphNode, input: INodeInputSlot): boolean {\n return this.renderLinks.some((link) => link.canConnectToInput(node, input))\n }\n\n isNodeValidDrop(node: LGraphNode): boolean {\n if (this.state.connectingTo === 'output') {\n return node.outputs.some((output) =>\n this.renderLinks.some((link) => link.canConnectToOutput(node, output))\n )\n }\n\n return node.inputs.some((input) =>\n this.renderLinks.some((link) => link.canConnectToInput(node, input))\n )\n }\n\n isSubgraphInputValidDrop(input: SubgraphInput): boolean {\n return this.renderLinks.some(\n (link) =>\n 'canConnectToSubgraphInput' in link &&\n link.canConnectToSubgraphInput(input)\n )\n }\n\n /**\n * Checks if a reroute is a valid drop target for any of the links being connected.\n * @param reroute The reroute that would be dropped on.\n * @returns `true` if any of the current links being connected are valid for the given reroute.\n */\n isRerouteValidDrop(reroute: Reroute): boolean {\n if (this.state.connectingTo === 'input') {\n const results = reroute.findTargetInputs()\n if (!results?.length) return false\n\n for (const { node, input } of results) {\n for (const renderLink of this.renderLinks) {\n if (renderLink.toType !== 'input') continue\n if (canConnectInputLinkToReroute(renderLink, node, input, reroute))\n return true\n }\n }\n } else {\n const result = reroute.findSourceOutput()\n if (!result) return false\n\n const { node, output } = result\n\n for (const renderLink of this.renderLinks) {\n if (renderLink.toType !== 'output') continue\n if (!renderLink.canConnectToReroute(reroute)) continue\n if (renderLink.canConnectToOutput(node, output)) return true\n }\n }\n\n return false\n }\n\n /** Sets connecting_links, used by some extensions still. */\n #setLegacyLinks(fromSlotIsInput: boolean): void {\n const links = this.renderLinks.map((link) => {\n const input = fromSlotIsInput ? (link.fromSlot as INodeInputSlot) : null\n const output = fromSlotIsInput ? null : (link.fromSlot as INodeOutputSlot)\n\n const afterRerouteId =\n link instanceof MovingLinkBase\n ? link.link?.parentId\n : link.fromReroute?.id\n\n return {\n node: link.node as LGraphNode,\n slot: link.fromSlotIndex,\n input,\n output,\n pos: link.fromPos,\n afterRerouteId\n } satisfies ConnectingLink\n })\n this.#setConnectingLinks(links)\n }\n\n /**\n * Exports the current state of the link connector.\n * @param network The network that the links being connected belong to.\n * @returns A POJO with the state of the link connector, links being connected, and their network.\n * @remarks Other than {@link network}, all properties are shallow cloned.\n */\n export(network: LinkNetwork): LinkConnectorExport {\n return {\n renderLinks: [...this.renderLinks],\n inputLinks: [...this.inputLinks],\n outputLinks: [...this.outputLinks],\n floatingLinks: [...this.floatingLinks],\n state: { ...this.state },\n network\n }\n }\n\n /**\n * Adds an event listener that will be automatically removed when the reset event is fired.\n * @param eventName The event to listen for.\n * @param listener The listener to call when the event is fired.\n */\n listenUntilReset<K extends keyof LinkConnectorEventMap>(\n eventName: K,\n listener: Parameters<typeof this.events.addEventListener<K>>[1],\n options?: Parameters<typeof this.events.addEventListener<K>>[2]\n ) {\n this.events.addEventListener(eventName, listener, options)\n this.events.addEventListener(\n 'reset',\n () => this.events.removeEventListener(eventName, listener),\n { once: true }\n )\n }\n\n /**\n * Resets everything to its initial state.\n *\n * Effectively cancels moving or connecting links.\n */\n reset(force = false): void {\n const mayContinue = this.events.dispatch('reset', force)\n if (mayContinue === false) return\n\n const {\n state,\n outputLinks,\n inputLinks,\n hiddenReroutes,\n renderLinks,\n floatingLinks\n } = this\n\n if (!force && state.connectingTo === undefined) return\n state.connectingTo = undefined\n\n for (const link of outputLinks) delete link._dragging\n for (const link of inputLinks) delete link._dragging\n for (const link of floatingLinks) delete link._dragging\n for (const reroute of hiddenReroutes) delete reroute._dragging\n\n renderLinks.length = 0\n inputLinks.length = 0\n outputLinks.length = 0\n floatingLinks.length = 0\n hiddenReroutes.clear()\n state.multi = false\n state.draggingExistingLinks = false\n state.snapLinksPos = undefined\n }\n}\n\n/** Validates that a single {@link RenderLink} can be dropped on the specified reroute. */\nfunction canConnectInputLinkToReroute(\n link:\n | ToInputRenderLink\n | MovingInputLink\n | FloatingRenderLink\n | ToInputFromIoNodeLink,\n inputNode: LGraphNode,\n input: INodeInputSlot,\n reroute: Reroute\n): boolean {\n const { fromReroute } = link\n\n if (\n !link.canConnectToInput(inputNode, input) ||\n // Would result in no change\n fromReroute?.id === reroute.id ||\n // Cannot connect from child to parent reroute\n fromReroute?.getReroutes()?.includes(reroute)\n ) {\n return false\n }\n\n // Would result in no change\n if (link instanceof ToInputRenderLink) {\n if (reroute.parentId == null) {\n // Link would make no change - output to reroute\n if (reroute.firstLink?.hasOrigin(link.node.id, link.fromSlotIndex))\n return false\n } else if (link.fromReroute?.id === reroute.parentId) {\n return false\n }\n }\n return true\n}\n","/**\n * Error thrown when infinite recursion is detected.\n */\nexport class RecursionError extends Error {\n constructor(subject: string) {\n super(subject)\n this.name = 'RecursionError'\n }\n}\n","export class InvalidLinkError extends Error {\n constructor(\n message: string = 'Attempted to access a link that was invalid.',\n cause?: Error\n ) {\n super(message, { cause })\n this.name = 'InvalidLinkError'\n }\n}\n","export class SlotIndexError extends Error {\n constructor(\n message: string = 'Attempted to access a slot that was out of bounds.',\n cause?: Error\n ) {\n super(message, { cause })\n this.name = 'SlotIndexError'\n }\n}\n","import type { LGraph } from '@/lib/litegraph/src/LGraph'\nimport type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'\nimport { InvalidLinkError } from '@/lib/litegraph/src/infrastructure/InvalidLinkError'\nimport { NullGraphError } from '@/lib/litegraph/src/infrastructure/NullGraphError'\nimport { RecursionError } from '@/lib/litegraph/src/infrastructure/RecursionError'\nimport { SlotIndexError } from '@/lib/litegraph/src/infrastructure/SlotIndexError'\nimport type {\n CallbackParams,\n CallbackReturn,\n ISlotType\n} from '@/lib/litegraph/src/interfaces'\nimport { LGraphEventMode, LiteGraph } from '@/lib/litegraph/src/litegraph'\n\nimport type { Subgraph } from './Subgraph'\nimport type { SubgraphNode } from './SubgraphNode'\n\nexport type ExecutionId = string\n\n/**\n * Interface describing the data transfer objects used when compiling a graph for execution.\n */\nexport type ExecutableLGraphNode = Omit<\n ExecutableNodeDTO,\n 'graph' | 'node' | 'subgraphNode'\n>\n\n/**\n * The end result of resolving a DTO input.\n * When a widget value is returned, {@link widgetInfo} is present and {@link origin_slot} is `-1`.\n */\ntype ResolvedInput = {\n /** DTO for the node that the link originates from. */\n node: ExecutableLGraphNode\n /** Full unique execution ID of the node that the link originates from. In the case of a widget value, this is the ID of the subgraph node. */\n origin_id: ExecutionId\n /** The slot index of the output on the node that the link originates from. `-1` when widget value is set. */\n origin_slot: number\n /** Boxed widget value (e.g. for widgets). If this box is `undefined`, then an input link is connected, and widget values from the subgraph node are ignored. */\n widgetInfo?: { value: unknown }\n}\n\n/**\n * Concrete implementation of {@link ExecutableLGraphNode}.\n * @remarks This is the class that is used to create the data transfer objects for executable nodes.\n */\nexport class ExecutableNodeDTO implements ExecutableLGraphNode {\n applyToGraph?(\n ...args: CallbackParams<typeof this.node.applyToGraph>\n ): CallbackReturn<typeof this.node.applyToGraph>\n\n /** The graph that this node is a part of. */\n readonly graph: LGraph | Subgraph\n\n inputs: { linkId: number | null; name: string; type: ISlotType }[]\n\n /** Backing field for {@link id}. */\n #id: ExecutionId\n\n /**\n * The path to the actual node through subgraph instances, represented as a list of all subgraph node IDs (instances),\n * followed by the actual original node ID within the subgraph. Each segment is separated by `:`.\n *\n * e.g. `1:2:3`:\n * - `1` is the node ID of the first subgraph node in the parent workflow\n * - `2` is the node ID of the second subgraph node in the first subgraph\n * - `3` is the node ID of the actual node in the subgraph definition\n */\n get id() {\n return this.#id\n }\n\n get type() {\n return this.node.type\n }\n\n get title() {\n return this.node.title\n }\n\n get mode() {\n return this.node.mode\n }\n\n get comfyClass() {\n return this.node.comfyClass\n }\n\n get isVirtualNode() {\n return this.node.isVirtualNode\n }\n\n get widgets() {\n return this.node.widgets\n }\n\n get subgraphId() {\n return this.subgraphNode?.subgraph.id\n }\n\n constructor(\n /** The actual node that this DTO wraps. */\n readonly node: LGraphNode | SubgraphNode,\n /** A list of subgraph instance node IDs from the root graph to the containing instance. @see {@link id} */\n readonly subgraphNodePath: readonly NodeId[],\n /** A flattened map of all DTOs in this node network. Subgraph instances have been expanded into their inner nodes. */\n readonly nodesByExecutionId: Map<ExecutionId, ExecutableLGraphNode>,\n /** The actual subgraph instance that contains this node, otherwise undefined. */\n readonly subgraphNode?: SubgraphNode\n ) {\n if (!node.graph) throw new NullGraphError()\n\n // Set the internal ID of the DTO\n this.#id = [...this.subgraphNodePath, this.node.id].join(':')\n this.graph = node.graph\n this.inputs = this.node.inputs.map((x) => ({\n linkId: x.link,\n name: x.name,\n type: x.type\n }))\n\n // Only create a wrapper if the node has an applyToGraph method\n if (this.node.applyToGraph) {\n this.applyToGraph = (...args) => this.node.applyToGraph?.(...args)\n }\n }\n\n /** Returns either the DTO itself, or the DTOs of the inner nodes of the subgraph. */\n getInnerNodes(): ExecutableLGraphNode[] {\n return this.node.isSubgraphNode()\n ? this.node.getInnerNodes(this.nodesByExecutionId, this.subgraphNodePath)\n : [this]\n }\n\n /**\n * Resolves the executable node & link IDs for a given input slot.\n * @param slot The slot index of the input.\n * @param visited Leave empty unless overriding this method.\n * A set of unique IDs, used to guard against infinite recursion.\n * If overriding, ensure that the set is passed on all recursive calls.\n * @returns The node and the origin ID / slot index of the output.\n */\n resolveInput(\n slot: number,\n visited = new Set<string>(),\n type?: ISlotType\n ): ResolvedInput | undefined {\n const uniqueId = `${this.subgraphNode?.subgraph.id}:${this.node.id}[I]${slot}`\n if (visited.has(uniqueId)) {\n const nodeInfo = `${this.node.id}${this.node.title ? ` (${this.node.title})` : ''}`\n const pathInfo =\n this.subgraphNodePath.length > 0\n ? ` at path ${this.subgraphNodePath.join(':')}`\n : ''\n throw new RecursionError(\n `Circular reference detected while resolving input ${slot} of node ${nodeInfo}${pathInfo}. ` +\n `This creates an infinite loop in link resolution. UniqueID: [${uniqueId}]`\n )\n }\n visited.add(uniqueId)\n\n const input = this.inputs.at(slot)\n if (!input)\n throw new SlotIndexError(\n `No input found for flattened id [${this.id}] slot [${slot}]`\n )\n\n // Nothing connected\n if (input.linkId == null) return\n\n const link = this.graph.getLink(input.linkId)\n if (!link)\n throw new InvalidLinkError(\n `No link found in parent graph for id [${this.id}] slot [${slot}] ${input.name}`\n )\n\n const { subgraphNode } = this\n\n // Link goes up and out of this subgraph\n if (subgraphNode && link.originIsIoNode) {\n const subgraphNodeInput = subgraphNode.inputs.at(link.origin_slot)\n if (!subgraphNodeInput)\n throw new SlotIndexError(\n `No input found for slot [${link.origin_slot}] ${input.name}`\n )\n\n // Nothing connected\n const linkId = subgraphNodeInput.link\n if (linkId == null) {\n const widget = subgraphNode.getWidgetFromSlot(subgraphNodeInput)\n if (!widget) return\n\n // Special case: SubgraphNode widget.\n return {\n node: this,\n origin_id: this.id,\n origin_slot: -1,\n widgetInfo: { value: widget.value }\n }\n }\n\n const outerLink = subgraphNode.graph.getLink(linkId)\n if (!outerLink)\n throw new InvalidLinkError(\n `No outer link found for slot [${link.origin_slot}] ${input.name}`\n )\n\n const subgraphNodeExecutionId = this.subgraphNodePath.join(':')\n const subgraphNodeDto = this.nodesByExecutionId.get(\n subgraphNodeExecutionId\n )\n if (!subgraphNodeDto)\n throw new Error(\n `No subgraph node DTO found for id [${subgraphNodeExecutionId}]`\n )\n\n return subgraphNodeDto.resolveInput(outerLink.target_slot, visited)\n }\n\n // Not part of a subgraph; use the original link\n const outputNode = this.graph.getNodeById(link.origin_id)\n if (!outputNode)\n throw new InvalidLinkError(\n `No input node found for id [${this.id}] slot [${slot}] ${input.name}`\n )\n\n const outputNodeExecutionId = [\n ...this.subgraphNodePath,\n outputNode.id\n ].join(':')\n const outputNodeDto = this.nodesByExecutionId.get(outputNodeExecutionId)\n if (!outputNodeDto)\n throw new Error(\n `No output node DTO found for id [${outputNodeExecutionId}]`\n )\n\n return outputNodeDto.resolveOutput(\n link.origin_slot,\n type ?? input.type,\n visited\n )\n }\n\n /**\n * Determines whether this output is a valid endpoint for a link (non-virtual, non-bypass).\n * @param slot The slot index of the output.\n * @param type The type of the input\n * @param visited A set of unique IDs to guard against infinite recursion. See {@link resolveInput}.\n * @returns The node and the origin ID / slot index of the output.\n */\n resolveOutput(\n slot: number,\n type: ISlotType,\n visited: Set<string>\n ): ResolvedInput | undefined {\n const uniqueId = `${this.subgraphNode?.subgraph.id}:${this.node.id}[O]${slot}`\n if (visited.has(uniqueId)) {\n const nodeInfo = `${this.node.id}${this.node.title ? ` (${this.node.title})` : ''}`\n const pathInfo =\n this.subgraphNodePath.length > 0\n ? ` at path ${this.subgraphNodePath.join(':')}`\n : ''\n throw new RecursionError(\n `Circular reference detected while resolving output ${slot} of node ${nodeInfo}${pathInfo}. ` +\n `This creates an infinite loop in link resolution. UniqueID: [${uniqueId}]`\n )\n }\n visited.add(uniqueId)\n\n // Upstreamed: Bypass nodes are bypassed using the first input with matching type\n if (this.mode === LGraphEventMode.BYPASS) {\n // Bypass nodes by finding first input with matching type\n const matchingIndex = this.#getBypassSlotIndex(slot, type)\n\n // No input types match - bypass not possible\n if (matchingIndex === -1) {\n console.warn(\n `[ExecutableNodeDTO.resolveOutput] No input types match type [${type}] for id [${this.id}] slot [${slot}]`,\n this\n )\n return\n }\n\n return this.resolveInput(matchingIndex, visited)\n }\n\n const { node } = this\n if (node.isSubgraphNode())\n return this.#resolveSubgraphOutput(slot, type, visited)\n\n if (node.isVirtualNode) {\n const virtualLink = this.node.getInputLink(slot)\n if (virtualLink) {\n const { inputNode } = virtualLink.resolve(this.graph)\n if (!inputNode)\n throw new InvalidLinkError(\n `Virtual node failed to resolve parent [${this.id}] slot [${slot}]`\n )\n\n const inputNodeExecutionId = [\n ...this.subgraphNodePath,\n inputNode.id\n ].join(':')\n const inputNodeDto = this.nodesByExecutionId.get(inputNodeExecutionId)\n if (!inputNodeDto)\n throw new Error(`No input node DTO found for id [${inputNode.id}]`)\n\n return inputNodeDto.resolveInput(virtualLink.target_slot, visited, type)\n }\n\n // Virtual nodes without a matching input should be discarded.\n return\n }\n\n return {\n node: this,\n origin_id: this.id,\n origin_slot: slot\n }\n }\n\n /**\n * Finds the index of the input slot on this node that matches the given output {@link slot} index.\n * Used when bypassing nodes.\n * @param slot The output slot index on this node\n * @param type The type of the final target input (so type list matches are accurate)\n * @returns The index of the input slot on this node, otherwise `-1`.\n */\n #getBypassSlotIndex(slot: number, type: ISlotType) {\n const { inputs } = this\n const oppositeInput = inputs[slot]\n const outputType = this.node.outputs[slot].type\n\n // Any type short circuit - match slot ID, fallback to first slot\n if (type === '*' || type === '') {\n return inputs.length > slot ? slot : 0\n }\n\n // Prefer input with the same slot ID\n if (\n oppositeInput &&\n LiteGraph.isValidConnection(oppositeInput.type, outputType) &&\n LiteGraph.isValidConnection(oppositeInput.type, type)\n ) {\n return slot\n }\n\n // Preserve legacy behaviour; use exact match first.\n const exactMatch = inputs.findIndex((input) => input.type === type)\n if (exactMatch !== -1) return exactMatch\n\n // Find first matching slot - prefer exact type\n return inputs.findIndex(\n (input) =>\n LiteGraph.isValidConnection(input.type, outputType) &&\n LiteGraph.isValidConnection(input.type, type)\n )\n }\n\n /**\n * Resolves the link inside a subgraph node, from the subgraph IO node to the node inside the subgraph.\n * @param slot The slot index of the output on the subgraph node.\n * @param visited A set of unique IDs to guard against infinite recursion. See {@link resolveInput}.\n * @returns A DTO for the node, and the origin ID / slot index of the output.\n */\n #resolveSubgraphOutput(\n slot: number,\n type: ISlotType,\n visited: Set<string>\n ): ResolvedInput | undefined {\n const { node } = this\n const output = node.outputs.at(slot)\n\n if (!output)\n throw new SlotIndexError(\n `No output found for flattened id [${this.id}] slot [${slot}]`\n )\n if (!node.isSubgraphNode())\n throw new TypeError(`Node is not a subgraph node: ${node.id}`)\n\n // Link inside the subgraph\n const innerResolved = node.resolveSubgraphOutputLink(slot)\n if (!innerResolved) return\n\n const innerNode = innerResolved.outputNode\n if (!innerNode)\n throw new Error(\n `No output node found for id [${this.id}] slot [${slot}] ${output.name}`\n )\n\n // Recurse into the subgraph\n const innerNodeExecutionId = [\n ...this.subgraphNodePath,\n node.id,\n innerNode.id\n ].join(':')\n const innerNodeDto = this.nodesByExecutionId.get(innerNodeExecutionId)\n if (!innerNodeDto)\n throw new Error(\n `No inner node DTO found for id [${innerNodeExecutionId}]`\n )\n\n return innerNodeDto.resolveOutput(\n innerResolved.link.origin_slot,\n type,\n visited\n )\n }\n}\n","import type { BaseLGraph, LGraph } from '@/lib/litegraph/src/LGraph'\nimport type { LGraphButton } from '@/lib/litegraph/src/LGraphButton'\nimport type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'\nimport { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport type { DrawTitleBoxOptions } from '@/lib/litegraph/src/LGraphNode'\nimport { LLink } from '@/lib/litegraph/src/LLink'\nimport type { ResolvedConnection } from '@/lib/litegraph/src/LLink'\nimport { RecursionError } from '@/lib/litegraph/src/infrastructure/RecursionError'\nimport type {\n ISubgraphInput,\n IWidgetLocator\n} from '@/lib/litegraph/src/interfaces'\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\nimport type {\n INodeInputSlot,\n ISlotType,\n NodeId\n} from '@/lib/litegraph/src/litegraph'\nimport { NodeInputSlot } from '@/lib/litegraph/src/node/NodeInputSlot'\nimport { NodeOutputSlot } from '@/lib/litegraph/src/node/NodeOutputSlot'\nimport type {\n GraphOrSubgraph,\n Subgraph\n} from '@/lib/litegraph/src/subgraph/Subgraph'\nimport type {\n ExportedSubgraphInstance,\n ISerialisedNode\n} from '@/lib/litegraph/src/types/serialisation'\nimport type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'\nimport type { UUID } from '@/lib/litegraph/src/utils/uuid'\nimport { AssetWidget } from '@/lib/litegraph/src/widgets/AssetWidget'\nimport { toConcreteWidget } from '@/lib/litegraph/src/widgets/widgetMap'\n\nimport { ExecutableNodeDTO } from './ExecutableNodeDTO'\nimport type { ExecutableLGraphNode, ExecutionId } from './ExecutableNodeDTO'\nimport type { SubgraphInput } from './SubgraphInput'\n\nconst workflowSvg = new Image()\nworkflowSvg.src =\n \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3E%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='white' stroke-linecap='round' stroke-width='1.3' d='M9.18613 3.09999H6.81377M9.18613 12.9H7.55288c-3.08678 0-5.35171-2.99581-4.60305-6.08843l.3054-1.26158M14.7486 2.1721l-.5931 2.45c-.132.54533-.6065.92789-1.1508.92789h-2.2993c-.77173 0-1.33797-.74895-1.1508-1.5221l.5931-2.45c.132-.54533.6065-.9279 1.1508-.9279h2.2993c.7717 0 1.3379.74896 1.1508 1.52211Zm-8.3033 0-.59309 2.45c-.13201.54533-.60646.92789-1.15076.92789H2.4021c-.7717 0-1.33793-.74895-1.15077-1.5221l.59309-2.45c.13201-.54533.60647-.9279 1.15077-.9279h2.29935c.77169 0 1.33792.74896 1.15076 1.52211Zm8.3033 9.8-.5931 2.45c-.132.5453-.6065.9279-1.1508.9279h-2.2993c-.77173 0-1.33797-.749-1.1508-1.5221l.5931-2.45c.132-.5453.6065-.9279 1.1508-.9279h2.2993c.7717 0 1.3379.7489 1.1508 1.5221Z'/%3E%3C/svg%3E %3C/svg%3E\"\n\n/**\n * An instance of a {@link Subgraph}, displayed as a node on the containing (parent) graph.\n */\nexport class SubgraphNode extends LGraphNode implements BaseLGraph {\n declare inputs: (INodeInputSlot & Partial<ISubgraphInput>)[]\n\n override readonly type: UUID\n override readonly isVirtualNode = true as const\n\n get rootGraph(): LGraph {\n return this.graph.rootGraph\n }\n\n override get displayType(): string {\n return 'Subgraph node'\n }\n\n override isSubgraphNode(): this is SubgraphNode {\n return true\n }\n\n override widgets: IBaseWidget[] = []\n\n /** Manages lifecycle of all subgraph event listeners */\n #eventAbortController = new AbortController()\n\n constructor(\n /** The (sub)graph that contains this subgraph instance. */\n override readonly graph: GraphOrSubgraph,\n /** The definition of this subgraph; how its nodes are configured, etc. */\n readonly subgraph: Subgraph,\n instanceData: ExportedSubgraphInstance\n ) {\n super(subgraph.name, subgraph.id)\n\n // Update this node when the subgraph input / output slots are changed\n const subgraphEvents = this.subgraph.events\n const { signal } = this.#eventAbortController\n\n subgraphEvents.addEventListener(\n 'input-added',\n (e) => {\n const subgraphInput = e.detail.input\n const { name, type } = subgraphInput\n const existingInput = this.inputs.find((i) => i.name == name)\n if (existingInput) {\n const linkId = subgraphInput.linkIds[0]\n const { inputNode, input } = subgraph.links[linkId].resolve(subgraph)\n const widget = inputNode?.widgets?.find?.((w) => w.name == name)\n if (widget)\n this.#setWidget(subgraphInput, existingInput, widget, input?.widget)\n return\n }\n const input = this.addInput(name, type)\n\n this.#addSubgraphInputListeners(subgraphInput, input)\n },\n { signal }\n )\n\n subgraphEvents.addEventListener(\n 'removing-input',\n (e) => {\n const widget = e.detail.input._widget\n if (widget) this.ensureWidgetRemoved(widget)\n\n this.removeInput(e.detail.index)\n this.setDirtyCanvas(true, true)\n },\n { signal }\n )\n\n subgraphEvents.addEventListener(\n 'output-added',\n (e) => {\n const { name, type } = e.detail.output\n this.addOutput(name, type)\n },\n { signal }\n )\n\n subgraphEvents.addEventListener(\n 'removing-output',\n (e) => {\n this.removeOutput(e.detail.index)\n this.setDirtyCanvas(true, true)\n },\n { signal }\n )\n\n subgraphEvents.addEventListener(\n 'renaming-input',\n (e) => {\n const { index, newName } = e.detail\n const input = this.inputs.at(index)\n if (!input) throw new Error('Subgraph input not found')\n\n input.label = newName\n if (input._widget) {\n input._widget.label = newName\n }\n },\n { signal }\n )\n\n subgraphEvents.addEventListener(\n 'renaming-output',\n (e) => {\n const { index, newName } = e.detail\n const output = this.outputs.at(index)\n if (!output) throw new Error('Subgraph output not found')\n\n output.label = newName\n },\n { signal }\n )\n\n this.type = subgraph.id\n this.configure(instanceData)\n\n this.addTitleButton({\n name: 'enter_subgraph',\n text: '\\uE93B', // Unicode for pi-window-maximize\n yOffset: 0, // No vertical offset needed, button is centered\n xOffset: -10,\n fontSize: 16\n })\n }\n\n override onTitleButtonClick(\n button: LGraphButton,\n canvas: LGraphCanvas\n ): void {\n if (button.name === 'enter_subgraph') {\n canvas.openSubgraph(this.subgraph, this)\n } else {\n super.onTitleButtonClick(button, canvas)\n }\n }\n\n #addSubgraphInputListeners(\n subgraphInput: SubgraphInput,\n input: INodeInputSlot & Partial<ISubgraphInput>\n ) {\n if (\n input._listenerController &&\n typeof input._listenerController.abort === 'function'\n ) {\n input._listenerController.abort()\n }\n input._listenerController = new AbortController()\n const { signal } = input._listenerController\n\n subgraphInput.events.addEventListener(\n 'input-connected',\n (e) => {\n if (input._widget) return\n\n const widget = subgraphInput._widget\n if (!widget) return\n\n const widgetLocator = e.detail.input.widget\n this.#setWidget(subgraphInput, input, widget, widgetLocator)\n },\n { signal }\n )\n\n subgraphInput.events.addEventListener(\n 'input-disconnected',\n () => {\n // If the input is connected to more than one widget, don't remove the widget\n const connectedWidgets = subgraphInput.getConnectedWidgets()\n if (connectedWidgets.length > 0) return\n\n this.removeWidgetByName(input.name)\n\n delete input.pos\n delete input.widget\n input._widget = undefined\n },\n { signal }\n )\n }\n\n override configure(info: ExportedSubgraphInstance): void {\n for (const input of this.inputs) {\n if (\n input._listenerController &&\n typeof input._listenerController.abort === 'function'\n ) {\n input._listenerController.abort()\n }\n }\n\n this.inputs.length = 0\n this.inputs.push(\n ...this.subgraph.inputNode.slots.map(\n (slot) =>\n new NodeInputSlot(\n {\n name: slot.name,\n localized_name: slot.localized_name,\n label: slot.label,\n type: slot.type,\n link: null\n },\n this\n )\n )\n )\n\n this.outputs.length = 0\n this.outputs.push(\n ...this.subgraph.outputNode.slots.map(\n (slot) =>\n new NodeOutputSlot(\n {\n name: slot.name,\n localized_name: slot.localized_name,\n label: slot.label,\n type: slot.type,\n links: null\n },\n this\n )\n )\n )\n\n super.configure(info)\n }\n\n override _internalConfigureAfterSlots() {\n // Reset widgets\n this.widgets.length = 0\n\n // Check all inputs for connected widgets\n for (const input of this.inputs) {\n const subgraphInput = this.subgraph.inputNode.slots.find(\n (slot) => slot.name === input.name\n )\n if (!subgraphInput) {\n // Skip inputs that don't exist in the subgraph definition\n // This can happen when loading workflows with dynamically added inputs\n console.warn(\n `[SubgraphNode.configure] No subgraph input found for input ${input.name}, skipping`\n )\n continue\n }\n\n this.#addSubgraphInputListeners(subgraphInput, input)\n\n // Find the first widget that this slot is connected to\n for (const linkId of subgraphInput.linkIds) {\n const link = this.subgraph.getLink(linkId)\n if (!link) {\n console.warn(\n `[SubgraphNode.configure] No link found for link ID ${linkId}`,\n this\n )\n continue\n }\n\n const { inputNode } = link.resolve(this.subgraph)\n if (!inputNode) {\n console.warn('Failed to resolve inputNode', link, this)\n continue\n }\n\n //Manually find input since target_slot can't be trusted\n const targetInput = inputNode.inputs.find((inp) => inp.link === linkId)\n if (!targetInput) {\n console.warn('Failed to find corresponding input', link, inputNode)\n continue\n }\n\n // No widget - ignore this link\n const widget = inputNode.getWidgetFromSlot(targetInput)\n if (!widget) continue\n\n this.#setWidget(subgraphInput, input, widget, targetInput.widget)\n break\n }\n }\n }\n\n #setWidget(\n subgraphInput: Readonly<SubgraphInput>,\n input: INodeInputSlot,\n widget: Readonly<IBaseWidget>,\n inputWidget: IWidgetLocator | undefined\n ) {\n // Use the first matching widget\n const promotedWidget = toConcreteWidget(widget, this).createCopyForNode(\n this\n )\n if (widget instanceof AssetWidget)\n promotedWidget.options.nodeType ??= widget.node.type\n\n Object.assign(promotedWidget, {\n get name() {\n return subgraphInput.name\n },\n set name(value) {\n console.warn(\n 'Promoted widget: setting name is not allowed',\n this,\n value\n )\n },\n get localized_name() {\n return subgraphInput.localized_name\n },\n set localized_name(value) {\n console.warn(\n 'Promoted widget: setting localized_name is not allowed',\n this,\n value\n )\n },\n get label() {\n return subgraphInput.label\n },\n set label(value) {\n console.warn(\n 'Promoted widget: setting label is not allowed',\n this,\n value\n )\n },\n get tooltip() {\n // Preserve the original widget's tooltip for promoted widgets\n return widget.tooltip\n },\n set tooltip(value) {\n console.warn(\n 'Promoted widget: setting tooltip is not allowed',\n this,\n value\n )\n }\n })\n\n const widgetCount = this.inputs.filter((i) => i.widget).length\n this.widgets.splice(widgetCount, 0, promotedWidget)\n\n // Dispatch widget-promoted event\n this.subgraph.events.dispatch('widget-promoted', {\n widget: promotedWidget,\n subgraphNode: this\n })\n\n // NOTE: This code creates linked chains of prototypes for passing across\n // multiple levels of subgraphs. As part of this, it intentionally avoids\n // creating new objects. Have care when making changes.\n input.widget ??= { name: subgraphInput.name }\n input.widget.name = subgraphInput.name\n if (inputWidget) Object.setPrototypeOf(input.widget, inputWidget)\n\n input._widget = promotedWidget\n }\n\n /**\n * Ensures the subgraph slot is in the params before adding the input as normal.\n * @param name The name of the input slot.\n * @param type The type of the input slot.\n * @param inputProperties Properties that are directly assigned to the created input. Default: a new, empty object.\n * @returns The new input slot.\n * @remarks Assertion is required to instantiate empty generic POJO.\n */\n override addInput<TInput extends Partial<ISubgraphInput>>(\n name: string,\n type: ISlotType,\n inputProperties: TInput = {} as TInput\n ): INodeInputSlot & TInput {\n // Bypasses type narrowing on this.inputs\n return super.addInput(name, type, inputProperties)\n }\n\n override getInputLink(slot: number): LLink | null {\n // Output side: the link from inside the subgraph\n const innerLink = this.subgraph.outputNode.slots[slot].getLinks().at(0)\n if (!innerLink) {\n console.warn(\n `SubgraphNode.getInputLink: no inner link found for slot ${slot}`\n )\n return null\n }\n\n const newLink = LLink.create(innerLink)\n newLink.origin_id = `${this.id}:${innerLink.origin_id}`\n newLink.origin_slot = innerLink.origin_slot\n\n return newLink\n }\n\n /**\n * Finds the internal links connected to the given input slot inside the subgraph, and resolves the nodes / slots.\n * @param slot The slot index\n * @returns The resolved connections, or undefined if no input node is found.\n * @remarks This is used to resolve the input links when dragging a link from a subgraph input slot.\n */\n resolveSubgraphInputLinks(slot: number): ResolvedConnection[] {\n const inputSlot = this.subgraph.inputNode.slots[slot]\n const innerLinks = inputSlot.getLinks()\n if (innerLinks.length === 0) {\n console.warn(\n `[SubgraphNode.resolveSubgraphInputLinks] No inner links found for input slot [${slot}] ${inputSlot.name}`,\n this\n )\n return []\n }\n return innerLinks.map((link) => link.resolve(this.subgraph))\n }\n\n /**\n * Finds the internal link connected to the given output slot inside the subgraph, and resolves the nodes / slots.\n * @param slot The slot index\n * @returns The output node if found, otherwise undefined.\n */\n resolveSubgraphOutputLink(slot: number): ResolvedConnection | undefined {\n const outputSlot = this.subgraph.outputNode.slots[slot]\n const innerLink = outputSlot.getLinks().at(0)\n if (innerLink) {\n return innerLink.resolve(this.subgraph)\n }\n console.warn(\n `[SubgraphNode.resolveSubgraphOutputLink] No inner link found for output slot [${slot}] ${outputSlot.name}`,\n this\n )\n }\n\n /** @internal Used to flatten the subgraph before execution. */\n override getInnerNodes(\n /** The set of computed node DTOs for this execution. */\n executableNodes: Map<ExecutionId, ExecutableLGraphNode>,\n /** The path of subgraph node IDs. */\n subgraphNodePath: readonly NodeId[] = [],\n /** Internal recursion param. The list of nodes to add to. */\n nodes: ExecutableLGraphNode[] = [],\n /** Internal recursion param. The set of visited nodes. */\n visited = new Set<SubgraphNode>()\n ): ExecutableLGraphNode[] {\n if (visited.has(this)) {\n const nodeInfo = `${this.id}${this.title ? ` (${this.title})` : ''}`\n const subgraphInfo = `'${this.subgraph.name || 'Unnamed Subgraph'}'`\n const depth = subgraphNodePath.length\n throw new RecursionError(\n `Circular reference detected at depth ${depth} in node ${nodeInfo} of subgraph ${subgraphInfo}. ` +\n `This creates an infinite loop in the subgraph hierarchy.`\n )\n }\n visited.add(this)\n\n const subgraphInstanceIdPath = [...subgraphNodePath, this.id]\n\n // Store the subgraph node DTO\n const parentSubgraphNode = this.graph.rootGraph\n .resolveSubgraphIdPath(subgraphNodePath)\n .at(-1)\n const subgraphNodeDto = new ExecutableNodeDTO(\n this,\n subgraphNodePath,\n executableNodes,\n parentSubgraphNode\n )\n executableNodes.set(subgraphNodeDto.id, subgraphNodeDto)\n\n for (const node of this.subgraph.nodes) {\n if ('getInnerNodes' in node && node.getInnerNodes) {\n node.getInnerNodes(\n executableNodes,\n subgraphInstanceIdPath,\n nodes,\n new Set(visited)\n )\n } else {\n // Create minimal DTOs rather than cloning the node\n const aVeryRealNode = new ExecutableNodeDTO(\n node,\n subgraphInstanceIdPath,\n executableNodes,\n this\n )\n executableNodes.set(aVeryRealNode.id, aVeryRealNode)\n nodes.push(aVeryRealNode)\n }\n }\n return nodes\n }\n\n override removeWidgetByName(name: string): void {\n const widget = this.widgets.find((w) => w.name === name)\n if (widget) {\n this.subgraph.events.dispatch('widget-demoted', {\n widget,\n subgraphNode: this\n })\n }\n super.removeWidgetByName(name)\n }\n\n override ensureWidgetRemoved(widget: IBaseWidget): void {\n if (this.widgets.includes(widget)) {\n this.subgraph.events.dispatch('widget-demoted', {\n widget,\n subgraphNode: this\n })\n }\n super.ensureWidgetRemoved(widget)\n }\n\n override onRemoved(): void {\n // Clean up all subgraph event listeners\n this.#eventAbortController.abort()\n\n // Clean up all promoted widgets\n for (const widget of this.widgets) {\n if ('isProxyWidget' in widget && widget.isProxyWidget) continue\n this.subgraph.events.dispatch('widget-demoted', {\n widget,\n subgraphNode: this\n })\n }\n\n for (const input of this.inputs) {\n if (\n input._listenerController &&\n typeof input._listenerController.abort === 'function'\n ) {\n input._listenerController.abort()\n }\n }\n }\n override drawTitleBox(\n ctx: CanvasRenderingContext2D,\n {\n scale,\n low_quality = false,\n title_height = LiteGraph.NODE_TITLE_HEIGHT,\n box_size = 10\n }: DrawTitleBoxOptions\n ): void {\n if (this.onDrawTitleBox) {\n this.onDrawTitleBox(ctx, title_height, this.renderingSize, scale)\n return\n }\n ctx.save()\n ctx.fillStyle = '#3b82f6'\n ctx.beginPath()\n ctx.roundRect(6, -24.5, 22, 20, 5)\n ctx.fill()\n if (!low_quality) {\n ctx.translate(25, 23)\n ctx.scale(-1.5, 1.5)\n ctx.drawImage(workflowSvg, 0, -title_height, box_size, box_size)\n }\n ctx.restore()\n }\n\n /**\n * Synchronizes widget values from this SubgraphNode instance to the\n * corresponding widgets in the subgraph definition before serialization.\n * This ensures nested subgraph widget values are preserved when saving.\n */\n override serialize(): ISerialisedNode {\n // Sync widget values to subgraph definition before serialization\n for (let i = 0; i < this.widgets.length; i++) {\n const widget = this.widgets[i]\n const input = this.inputs.find((inp) => inp.name === widget.name)\n\n if (input) {\n const subgraphInput = this.subgraph.inputNode.slots.find(\n (slot) => slot.name === input.name\n )\n\n if (subgraphInput) {\n // Find all widgets connected to this subgraph input\n const connectedWidgets = subgraphInput.getConnectedWidgets()\n\n // Update the value of all connected widgets\n for (const connectedWidget of connectedWidgets) {\n connectedWidget.value = widget.value\n }\n }\n }\n }\n\n // Call parent serialize method\n return super.serialize()\n }\n override clone() {\n const clone = super.clone()\n // force reasign so domWidgets reset ownership\n\n this.properties.proxyWidgets = this.properties.proxyWidgets\n\n //TODO: Consider deep cloning subgraphs here.\n //It's the safest place to prevent creation of linked subgraphs\n //But the frequency of clone().serialize() calls is likely to result in\n //pollution of rootGraph.subgraphs\n\n return clone\n }\n}\n","import type { LGraphNode } from '../LGraphNode'\nimport type { Direction, IBoundaryNodes, NewNodePosition } from '../interfaces'\n\n/**\n * Finds the nodes that are farthest in all four directions, representing the boundary of the nodes.\n * @param nodes The nodes to check the edges of\n * @returns An object listing the furthest node (edge) in all four directions.\n * `null` if no nodes were supplied or the first node was falsy.\n */\nexport function getBoundaryNodes(nodes: LGraphNode[]): IBoundaryNodes | null {\n const valid = nodes?.find((x) => x)\n if (!valid) return null\n\n let top = valid\n let right = valid\n let bottom = valid\n let left = valid\n\n for (const node of nodes) {\n if (!node) continue\n const [x, y] = node.pos\n const [width, height] = node.size\n\n if (y < top.pos[1]) top = node\n if (x + width > right.pos[0] + right.size[0]) right = node\n if (y + height > bottom.pos[1] + bottom.size[1]) bottom = node\n if (x < left.pos[0]) left = node\n }\n\n return {\n top,\n right,\n bottom,\n left\n }\n}\n\n/**\n * Distributes nodes evenly along a horizontal or vertical plane.\n * @param nodes The nodes to distribute\n * @param horizontal If true, distributes along the horizontal plane. Otherwise, the vertical plane.\n */\nexport function distributeNodes(\n nodes: LGraphNode[],\n horizontal?: boolean\n): NewNodePosition[] {\n const nodeCount = nodes?.length\n if (!(nodeCount > 1)) return []\n\n const index = horizontal ? 0 : 1\n\n let total = 0\n let highest = -Infinity\n\n for (const node of nodes) {\n total += node.size[index]\n\n const high = node.pos[index] + node.size[index]\n if (high > highest) highest = high\n }\n const sorted = [...nodes].sort((a, b) => a.pos[index] - b.pos[index])\n const lowest = sorted[0].pos[index]\n\n const gap = (highest - lowest - total) / (nodeCount - 1)\n let startAt = lowest\n for (let i = 0; i < nodeCount; i++) {\n const node = sorted[i]\n node.pos[index] = startAt + gap * i\n startAt += node.size[index]\n }\n const newPositions = sorted.map(\n (node): NewNodePosition => ({\n node,\n newPos: {\n x: node.pos[0],\n y: node.pos[1]\n }\n })\n )\n return newPositions\n}\n\n/**\n * Aligns all nodes along the edge of a node.\n * @param nodes The nodes to align\n * @param direction The edge to align nodes on\n * @param align_to The node to align all other nodes to. If undefined, the farthest node will be used.\n */\nexport function alignNodes(\n nodes: LGraphNode[],\n direction: Direction,\n align_to?: LGraphNode\n): NewNodePosition[] {\n if (!nodes) return []\n\n const boundary =\n align_to === undefined\n ? getBoundaryNodes(nodes)\n : { top: align_to, right: align_to, bottom: align_to, left: align_to }\n\n if (boundary === null) return []\n\n const nodePositions = nodes.map((node): NewNodePosition => {\n switch (direction) {\n case 'right':\n return {\n node,\n newPos: {\n x: boundary.right.pos[0] + boundary.right.size[0] - node.size[0],\n y: node.pos[1]\n }\n }\n case 'left':\n return {\n node,\n newPos: {\n x: boundary.left.pos[0],\n y: node.pos[1]\n }\n }\n case 'top':\n return {\n node,\n newPos: {\n x: node.pos[0],\n y: boundary.top.pos[1]\n }\n }\n case 'bottom':\n return {\n node,\n newPos: {\n x: node.pos[0],\n y: boundary.bottom.pos[1] + boundary.bottom.size[1] - node.size[1]\n }\n }\n }\n })\n\n for (const { node, newPos } of nodePositions) {\n node.pos[0] = newPos.x\n node.pos[1] = newPos.y\n }\n return nodePositions\n}\n","import type { CanvasColour, ISlotType } from '../interfaces'\nimport { LiteGraph } from '../litegraph'\n\n/**\n * Resolve the colour used while rendering or previewing a connection of a given slot type.\n */\nexport function resolveConnectingLinkColor(\n type: ISlotType | undefined\n): CanvasColour {\n return type === LiteGraph.EVENT\n ? LiteGraph.EVENT_LINK_COLOR\n : LiteGraph.CONNECTING_LINK_COLOR\n}\n","import { toString } from 'es-toolkit/compat'\n\nimport { PREFIX, SEPARATOR } from '@/constants/groupNodeConstants'\nimport { LitegraphLinkAdapter } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'\nimport type { LinkRenderContext } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'\nimport { getSlotPosition } from '@/renderer/core/canvas/litegraph/slotCalculations'\nimport { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'\nimport { layoutStore } from '@/renderer/core/layout/store/layoutStore'\nimport { LayoutSource } from '@/renderer/core/layout/types'\nimport { removeNodeTitleHeight } from '@/renderer/core/layout/utils/nodeSizeUtil'\nimport { forEachNode } from '@/utils/graphTraversalUtil'\n\nimport { CanvasPointer } from './CanvasPointer'\nimport type { ContextMenu } from './ContextMenu'\nimport { DragAndScale } from './DragAndScale'\nimport type { AnimationOptions } from './DragAndScale'\nimport type { LGraph } from './LGraph'\nimport { LGraphGroup } from './LGraphGroup'\nimport { LGraphNode } from './LGraphNode'\nimport type { NodeId, NodeProperty } from './LGraphNode'\nimport { LLink } from './LLink'\nimport type { LinkId } from './LLink'\nimport { Reroute } from './Reroute'\nimport type { RerouteId } from './Reroute'\nimport { LinkConnector } from './canvas/LinkConnector'\nimport { isOverNodeInput, isOverNodeOutput } from './canvas/measureSlots'\nimport { strokeShape } from './draw'\nimport type {\n CustomEventDispatcher,\n ICustomEventTarget\n} from './infrastructure/CustomEventTarget'\nimport type { LGraphCanvasEventMap } from './infrastructure/LGraphCanvasEventMap'\nimport { NullGraphError } from './infrastructure/NullGraphError'\nimport { Rectangle } from './infrastructure/Rectangle'\nimport type {\n CanvasColour,\n ColorOption,\n ConnectingLink,\n ContextMenuDivElement,\n DefaultConnectionColors,\n Dictionary,\n Direction,\n IBoundaryNodes,\n IColorable,\n IContextMenuOptions,\n IContextMenuValue,\n INodeInputSlot,\n INodeOutputSlot,\n INodeSlot,\n INodeSlotContextItem,\n ISlotType,\n LinkNetwork,\n LinkSegment,\n NewNodePosition,\n NullableProperties,\n Panel,\n PanelButton,\n PanelWidget,\n PanelWidgetCallback,\n PanelWidgetOptions,\n Point,\n Positionable,\n ReadOnlyRect,\n Rect,\n Size\n} from './interfaces'\nimport { LiteGraph } from './litegraph'\nimport {\n containsRect,\n createBounds,\n distance,\n isInRect,\n isInRectangle,\n isPointInRect,\n overlapBounding,\n snapPoint\n} from './measure'\nimport { NodeInputSlot } from './node/NodeInputSlot'\nimport type { Subgraph } from './subgraph/Subgraph'\nimport { SubgraphIONodeBase } from './subgraph/SubgraphIONodeBase'\nimport type { SubgraphInputNode } from './subgraph/SubgraphInputNode'\nimport { SubgraphNode } from './subgraph/SubgraphNode'\nimport type { SubgraphOutputNode } from './subgraph/SubgraphOutputNode'\nimport type {\n CanvasPointerEvent,\n CanvasPointerExtensions\n} from './types/events'\nimport {\n CanvasItem,\n LGraphEventMode,\n LinkDirection,\n LinkMarkerShape,\n LinkRenderType,\n RenderShape,\n TitleMode\n} from './types/globalEnums'\nimport type {\n ClipboardItems,\n ISerialisedNode,\n SubgraphIO\n} from './types/serialisation'\nimport type { NeverNever, PickNevers } from './types/utility'\nimport type { IBaseWidget, TWidgetValue } from './types/widgets'\nimport { alignNodes, distributeNodes, getBoundaryNodes } from './utils/arrange'\nimport { findFirstNode, getAllNestedItems } from './utils/collections'\nimport { resolveConnectingLinkColor } from './utils/linkColors'\nimport { createUuidv4 } from './utils/uuid'\nimport type { UUID } from './utils/uuid'\nimport { BaseWidget } from './widgets/BaseWidget'\nimport { toConcreteWidget } from './widgets/widgetMap'\n\ninterface IShowSearchOptions {\n node_to?: LGraphNode | null\n node_from?: LGraphNode | null\n slot_from: number | INodeOutputSlot | INodeInputSlot | null | undefined\n type_filter_in?: ISlotType\n type_filter_out?: ISlotType | false\n\n // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out\n do_type_filter?: boolean\n show_general_if_none_on_typefilter?: boolean\n show_general_after_typefiltered?: boolean\n hide_on_mouse_leave?: boolean\n show_all_if_empty?: boolean\n show_all_on_open?: boolean\n}\n\ninterface ICreateNodeOptions {\n /** input */\n nodeFrom?: SubgraphInputNode | LGraphNode | null\n /** input */\n slotFrom?: number | INodeOutputSlot | INodeInputSlot | SubgraphIO | null\n /** output */\n nodeTo?: SubgraphOutputNode | LGraphNode | null\n /** output */\n slotTo?: number | INodeOutputSlot | INodeInputSlot | SubgraphIO | null\n /** pass the event coords */\n\n /** Create the connection from a reroute */\n afterRerouteId?: RerouteId\n\n // FIXME: Should not be optional\n /** choose a nodetype to add, AUTO to set at first good */\n nodeType?: string\n e?: CanvasPointerEvent\n allow_searchbox?: boolean\n}\n\ninterface ICreateDefaultNodeOptions extends ICreateNodeOptions {\n /** Position of new node */\n position: Point\n /** adjust x,y */\n posAdd?: Point\n /** alpha, adjust the position x,y based on the new node size w,h */\n posSizeFix?: Point\n}\n\ninterface HasShowSearchCallback {\n /** See {@link LGraphCanvas.showSearchBox} */\n showSearchBox: (\n event: MouseEvent | null,\n options?: IShowSearchOptions\n ) => HTMLDivElement | void\n}\n\ninterface ICloseable {\n close(): void\n}\n\ninterface IDialogExtensions extends ICloseable {\n modified(): void\n is_modified: boolean\n}\n\ninterface IDialog extends HTMLDivElement, IDialogExtensions {}\ntype PromptDialog = Omit<IDialog, 'modified'>\n\ninterface IDialogOptions {\n position?: Point\n event?: MouseEvent\n checkForInput?: boolean\n closeOnLeave?: boolean\n onclose?(): void\n}\n\n/** @inheritdoc {@link LGraphCanvas.state} */\ninterface LGraphCanvasState {\n /** {@link Positionable} items are being dragged on the canvas. */\n draggingItems: boolean\n /** The canvas itself is being dragged. */\n draggingCanvas: boolean\n /** The canvas is read-only, preventing changes to nodes, disconnecting links, moving items, etc. */\n readOnly: boolean\n\n /** Bit flags indicating what is currently below the pointer. */\n hoveringOver: CanvasItem\n /** If `true`, pointer move events will set the canvas cursor style. */\n shouldSetCursor: boolean\n\n /**\n * Dirty flag indicating that {@link selectedItems} has changed.\n * Downstream consumers may reset to false once actioned.\n */\n selectionChanged: boolean\n}\n\n/**\n * The items created by a clipboard paste operation.\n * Includes maps of original copied IDs to newly created items.\n */\ninterface ClipboardPasteResult {\n /** All successfully created items */\n created: Positionable[]\n /** Map: original node IDs to newly created nodes */\n nodes: Map<NodeId, LGraphNode>\n /** Map: original link IDs to new link IDs */\n links: Map<LinkId, LLink>\n /** Map: original reroute IDs to newly created reroutes */\n reroutes: Map<RerouteId, Reroute>\n /** Map: original subgraph IDs to newly created subgraphs */\n subgraphs: Map<UUID, Subgraph>\n}\n\n/** Options for {@link LGraphCanvas.pasteFromClipboard}. */\ninterface IPasteFromClipboardOptions {\n /** If `true`, always attempt to connect inputs of pasted nodes - including to nodes that were not pasted. */\n connectInputs?: boolean\n /** The position to paste the items at. */\n position?: Point\n}\n\ninterface ICreatePanelOptions {\n closable?: boolean\n window?: Window\n onOpen?: () => void\n onClose?: () => void\n width?: number | string\n height?: number | string\n}\n\ninterface SlotTypeDefaultNodeOpts {\n node?: string\n title?: string\n properties?: Record<string, NodeProperty>\n inputs?: [string, string][]\n outputs?: [string, string][]\n json?: Parameters<LGraphNode['configure']>[0]\n}\n\nconst cursors = {\n NE: 'nesw-resize',\n SE: 'nwse-resize',\n SW: 'nesw-resize',\n NW: 'nwse-resize'\n} as const\n\n// Optimised buffers used during rendering\nconst temp = new Rectangle()\nconst temp_vec2: Point = [0, 0]\nconst tmp_area = new Rectangle()\nconst margin_area = new Rectangle()\nconst link_bounding = new Rectangle()\n/**\n * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required.\n * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked\n */\nexport class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap> {\n static DEFAULT_BACKGROUND_IMAGE =\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII='\n\n static DEFAULT_EVENT_LINK_COLOR = '#A86'\n\n /** Link type to colour dictionary. */\n static link_type_colors: Dictionary<string> = {\n '-1': LGraphCanvas.DEFAULT_EVENT_LINK_COLOR,\n number: '#AAA',\n node: '#DCA'\n }\n\n static gradients: Record<string, CanvasGradient> = {}\n\n static search_limit = -1\n static node_colors: Record<string, ColorOption> = {\n red: { color: '#322', bgcolor: '#533', groupcolor: '#A88' },\n brown: { color: '#332922', bgcolor: '#593930', groupcolor: '#b06634' },\n green: { color: '#232', bgcolor: '#353', groupcolor: '#8A8' },\n blue: { color: '#223', bgcolor: '#335', groupcolor: '#88A' },\n pale_blue: {\n color: '#2a363b',\n bgcolor: '#3f5159',\n groupcolor: '#3f789e'\n },\n cyan: { color: '#233', bgcolor: '#355', groupcolor: '#8AA' },\n purple: { color: '#323', bgcolor: '#535', groupcolor: '#a1309b' },\n yellow: { color: '#432', bgcolor: '#653', groupcolor: '#b58b2a' },\n black: { color: '#222', bgcolor: '#000', groupcolor: '#444' }\n }\n\n /**\n * @internal Exclusively a workaround for design limitation in {@link LGraphNode.computeSize}.\n */\n static _measureText?: (text: string, fontStyle?: string) => number\n\n /**\n * The state of this canvas, e.g. whether it is being dragged, or read-only.\n *\n * Implemented as a POCO that can be proxied without side-effects.\n */\n state: LGraphCanvasState = {\n draggingItems: false,\n draggingCanvas: false,\n readOnly: false,\n hoveringOver: CanvasItem.Nothing,\n shouldSetCursor: true,\n selectionChanged: false\n }\n\n #subgraph?: Subgraph\n get subgraph(): Subgraph | undefined {\n return this.#subgraph\n }\n\n set subgraph(value: Subgraph | undefined) {\n if (value !== this.#subgraph) {\n this.#subgraph = value\n if (value)\n this.dispatch('litegraph:set-graph', {\n oldGraph: this.#subgraph,\n newGraph: value\n })\n }\n }\n\n /**\n * The location of the fps info widget. Leaving an element unset will use the default position for that element.\n */\n fpsInfoLocation:\n | [x: number | null | undefined, y: number | null | undefined]\n | null\n | undefined\n\n /** Dispatches a custom event on the canvas. */\n dispatch<T extends keyof NeverNever<LGraphCanvasEventMap>>(\n type: T,\n detail: LGraphCanvasEventMap[T]\n ): boolean\n dispatch<T extends keyof PickNevers<LGraphCanvasEventMap>>(type: T): boolean\n dispatch<T extends keyof LGraphCanvasEventMap>(\n type: T,\n detail?: LGraphCanvasEventMap[T]\n ) {\n const event = new CustomEvent(type as string, { detail, bubbles: true })\n return this.canvas.dispatchEvent(event)\n }\n\n dispatchEvent<TEvent extends keyof LGraphCanvasEventMap>(\n type: TEvent,\n detail: LGraphCanvasEventMap[TEvent]\n ) {\n this.canvas.dispatchEvent(new CustomEvent(type, { detail }))\n }\n\n #updateCursorStyle() {\n if (!this.state.shouldSetCursor) return\n\n const crosshairItems =\n CanvasItem.Node |\n CanvasItem.RerouteSlot |\n CanvasItem.SubgraphIoNode |\n CanvasItem.SubgraphIoSlot\n\n let cursor = 'default'\n if (this.state.draggingCanvas) {\n cursor = 'grabbing'\n } else if (this.state.readOnly) {\n cursor = 'grab'\n } else if (this.pointer.resizeDirection) {\n cursor = cursors[this.pointer.resizeDirection] ?? cursors.SE\n } else if (this.state.hoveringOver & crosshairItems) {\n cursor = 'crosshair'\n } else if (this.state.hoveringOver & CanvasItem.Reroute) {\n cursor = 'grab'\n }\n\n this.canvas.style.cursor = cursor\n }\n\n // Whether the canvas was previously being dragged prior to pressing space key.\n // null if space key is not pressed.\n private _previously_dragging_canvas: boolean | null = null\n\n // #region Legacy accessors\n /** @deprecated @inheritdoc {@link LGraphCanvasState.readOnly} */\n get read_only(): boolean {\n return this.state.readOnly\n }\n\n set read_only(value: boolean) {\n this.state.readOnly = value\n this.#updateCursorStyle()\n }\n\n get isDragging(): boolean {\n return this.state.draggingItems\n }\n\n set isDragging(value: boolean) {\n this.state.draggingItems = value\n }\n\n get hoveringOver(): CanvasItem {\n return this.state.hoveringOver\n }\n\n set hoveringOver(value: CanvasItem) {\n this.state.hoveringOver = value\n this.#updateCursorStyle()\n }\n\n /** @deprecated Replace all references with {@link pointer}.{@link CanvasPointer.isDown isDown}. */\n get pointer_is_down() {\n return this.pointer.isDown\n }\n\n /** @deprecated Replace all references with {@link pointer}.{@link CanvasPointer.isDouble isDouble}. */\n get pointer_is_double() {\n return this.pointer.isDouble\n }\n\n /** @deprecated @inheritdoc {@link LGraphCanvasState.draggingCanvas} */\n get dragging_canvas(): boolean {\n return this.state.draggingCanvas\n }\n\n set dragging_canvas(value: boolean) {\n this.state.draggingCanvas = value\n this.#updateCursorStyle()\n }\n\n /**\n * @deprecated Use {@link LGraphNode.titleFontStyle} instead.\n */\n get title_text_font(): string {\n return `${LiteGraph.NODE_TEXT_SIZE}px ${LiteGraph.NODE_FONT}`\n }\n // #endregion Legacy accessors\n\n get inner_text_font(): string {\n return `normal ${LiteGraph.NODE_SUBTEXT_SIZE}px ${LiteGraph.NODE_FONT}`\n }\n\n #maximumFrameGap = 0\n /** Maximum frames per second to render. 0: unlimited. Default: 0 */\n public get maximumFps() {\n return this.#maximumFrameGap > Number.EPSILON\n ? this.#maximumFrameGap / 1000\n : 0\n }\n\n public set maximumFps(value) {\n this.#maximumFrameGap = value > Number.EPSILON ? 1000 / value : 0\n }\n\n /**\n * @deprecated Use {@link LiteGraphGlobal.ROUND_RADIUS} instead.\n */\n get round_radius() {\n return LiteGraph.ROUND_RADIUS\n }\n\n /**\n * @deprecated Use {@link LiteGraphGlobal.ROUND_RADIUS} instead.\n */\n set round_radius(value: number) {\n LiteGraph.ROUND_RADIUS = value\n }\n\n // Cached LOD threshold values for performance\n private _lowQualityZoomThreshold: number = 0\n private _isLowQuality: boolean = false\n\n /**\n * Updates the low quality zoom threshold based on current settings.\n * Called when min_font_size_for_lod or DPR changes.\n */\n private updateLowQualityThreshold(): void {\n if (this._min_font_size_for_lod === 0) {\n // LOD disabled\n this._lowQualityZoomThreshold = 0\n this._isLowQuality = false\n return\n }\n\n const baseFontSize = LiteGraph.NODE_TEXT_SIZE // 14px\n const dprAdjustment = Math.sqrt(window.devicePixelRatio || 1) //Using sqrt here because higher DPR monitors do not linearily scale the readability of the font, instead they increase the font by some heurisitc, and to approximate we use sqrt to say basically a DPR of 2 increases the readability by 40%, 3 by 70%\n\n // Calculate the zoom level where text becomes unreadable\n this._lowQualityZoomThreshold =\n this._min_font_size_for_lod / (baseFontSize * dprAdjustment)\n\n // Update current state based on current zoom\n this._isLowQuality = this.ds.scale < this._lowQualityZoomThreshold\n }\n\n /**\n * Render low quality when zoomed out based on minimum readable font size.\n */\n get low_quality(): boolean {\n return this._isLowQuality\n }\n\n options: {\n skip_events?: boolean\n viewport?: Rect\n skip_render?: boolean\n autoresize?: boolean\n }\n\n background_image: string\n readonly ds: DragAndScale\n readonly pointer: CanvasPointer\n zoom_modify_alpha: boolean\n zoom_speed: number\n node_title_color: string\n default_link_color: string\n default_connection_color: {\n input_off: string\n input_on: string\n output_off: string\n output_on: string\n }\n\n default_connection_color_byType: Dictionary<CanvasColour>\n default_connection_color_byTypeOff: Dictionary<CanvasColour>\n\n /** Gets link colours. Extremely basic impl. until the legacy object dictionaries are removed. */\n colourGetter: DefaultConnectionColors = {\n getConnectedColor: (type: string) =>\n this.default_connection_color_byType[type] ||\n this.default_connection_color.output_on,\n getDisconnectedColor: (type: string) =>\n this.default_connection_color_byTypeOff[type] ||\n this.default_connection_color_byType[type] ||\n this.default_connection_color.output_off\n }\n\n highquality_render: boolean\n use_gradients: boolean\n editor_alpha: number\n pause_rendering: boolean\n clear_background: boolean\n clear_background_color: string\n render_only_selected: boolean\n show_info: boolean\n allow_dragcanvas: boolean\n allow_dragnodes: boolean\n allow_interaction: boolean\n multi_select: boolean\n allow_searchbox: boolean\n allow_reconnect_links: boolean\n align_to_grid: boolean\n drag_mode: boolean\n dragging_rectangle: Rect | null\n filter?: string | null\n set_canvas_dirty_on_mouse_event: boolean\n always_render_background: boolean\n render_shadows: boolean\n render_canvas_border: boolean\n render_connections_shadows: boolean\n render_connections_border: boolean\n render_curved_connections: boolean\n render_connection_arrows: boolean\n render_collapsed_slots: boolean\n render_execution_order: boolean\n render_link_tooltip: boolean\n\n /** Shape of the markers shown at the midpoint of links. Default: Circle */\n linkMarkerShape: LinkMarkerShape = LinkMarkerShape.Circle\n links_render_mode: number\n /** Minimum font size in pixels before switching to low quality rendering.\n * This initializes first and if we can't get the value from the settings we default to 8px\n */\n private _min_font_size_for_lod: number = 8\n\n get min_font_size_for_lod(): number {\n return this._min_font_size_for_lod\n }\n\n set min_font_size_for_lod(value: number) {\n if (this._min_font_size_for_lod !== value) {\n this._min_font_size_for_lod = value\n this.updateLowQualityThreshold()\n }\n }\n /** mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle */\n readonly mouse: Point\n /** mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle */\n readonly graph_mouse: Point\n /** @deprecated LEGACY: REMOVE THIS, USE {@link graph_mouse} INSTEAD */\n canvas_mouse: Point\n /** to personalize the search box */\n onSearchBox?: (\n helper: HTMLDivElement,\n str: string,\n canvas: LGraphCanvas\n ) => string[] | undefined\n onSearchBoxSelection?: (\n name: string,\n event: MouseEvent,\n canvas: LGraphCanvas\n ) => void\n onMouse?: (e: CanvasPointerEvent) => boolean\n /** to render background objects (behind nodes and connections) in the canvas affected by transform */\n onDrawBackground?: (\n ctx: CanvasRenderingContext2D,\n visible_area: Rectangle\n ) => void\n /** to render foreground objects (above nodes and connections) in the canvas affected by transform */\n onDrawForeground?: (\n ctx: CanvasRenderingContext2D,\n visible_area: Rectangle\n ) => void\n connections_width: number\n /** The current node being drawn by {@link drawNode}. This should NOT be used to determine the currently selected node. See {@link selectedItems} */\n current_node: LGraphNode | null\n /** used for widgets */\n node_widget?: [LGraphNode, IBaseWidget] | null\n /** The link to draw a tooltip for. */\n over_link_center?: LinkSegment\n last_mouse_position: Point\n /** The visible area of this canvas. Tightly coupled with {@link ds}. */\n visible_area: Rectangle\n /** Contains all links and reroutes that were rendered. Repopulated every render cycle. */\n renderedPaths: Set<LinkSegment> = new Set()\n /** @deprecated Replaced by {@link renderedPaths}, but length is set to 0 by some extensions. */\n visible_links: LLink[] = []\n /** @deprecated This array is populated and cleared to support legacy extensions. The contents are ignored by Litegraph. */\n connecting_links: ConnectingLink[] | null\n linkConnector = new LinkConnector((links) => (this.connecting_links = links))\n /** The viewport of this canvas. Tightly coupled with {@link ds}. */\n readonly viewport?: Rect\n autoresize: boolean\n static active_canvas: LGraphCanvas\n frame = 0\n last_draw_time = 0\n render_time = 0\n fps = 0\n /** @deprecated See {@link LGraphCanvas.selectedItems} */\n selected_nodes: Dictionary<LGraphNode> = {}\n /** All selected nodes, groups, and reroutes */\n selectedItems: Set<Positionable> = new Set()\n /** The group currently being resized. */\n resizingGroup: LGraphGroup | null = null\n /** @deprecated See {@link LGraphCanvas.selectedItems} */\n selected_group: LGraphGroup | null = null\n /** The nodes that are currently visible on the canvas. */\n visible_nodes: LGraphNode[] = []\n /**\n * The IDs of the nodes that are currently visible on the canvas. More\n * performant than {@link visible_nodes} for visibility checks.\n */\n #visible_node_ids: Set<NodeId> = new Set()\n node_over?: LGraphNode\n node_capturing_input?: LGraphNode | null\n highlighted_links: Dictionary<boolean> = {}\n\n #visibleReroutes: Set<Reroute> = new Set()\n\n dirty_canvas: boolean = true\n dirty_bgcanvas: boolean = true\n /** A map of nodes that require selective-redraw */\n dirty_nodes = new Map<NodeId, LGraphNode>()\n dirty_area?: Rect | null\n /** @deprecated Unused */\n node_in_panel?: LGraphNode | null\n last_mouse: Readonly<Point> = [0, 0]\n last_mouseclick: number = 0\n graph: LGraph | Subgraph | null\n get _graph(): LGraph | Subgraph {\n if (!this.graph) throw new NullGraphError()\n return this.graph\n }\n\n canvas: HTMLCanvasElement & ICustomEventTarget<LGraphCanvasEventMap>\n bgcanvas: HTMLCanvasElement\n ctx: CanvasRenderingContext2D\n _events_binded?: boolean\n _mousedown_callback?(e: PointerEvent): void\n _mousewheel_callback?(e: WheelEvent): void\n _mousemove_callback?(e: PointerEvent): void\n _mouseup_callback?(e: PointerEvent): void\n _mouseout_callback?(e: PointerEvent): void\n _mousecancel_callback?(e: PointerEvent): void\n _key_callback?(e: KeyboardEvent): void\n bgctx?: CanvasRenderingContext2D | null\n is_rendering?: boolean\n /** @deprecated Panels */\n block_click?: boolean\n /** @deprecated Panels */\n last_click_position?: Point | null\n resizing_node?: LGraphNode | null\n /** @deprecated See {@link LGraphCanvas.resizingGroup} */\n selected_group_resizing?: boolean\n /** @deprecated See {@link pointer}.{@link CanvasPointer.dragStarted dragStarted} */\n last_mouse_dragging?: boolean\n onMouseDown?: (arg0: CanvasPointerEvent) => void\n _highlight_pos?: Point\n _highlight_input?: INodeInputSlot\n // TODO: Check if panels are used\n /** @deprecated Panels */\n node_panel?: Panel\n /** @deprecated Panels */\n options_panel?: Panel\n _bg_img?: HTMLImageElement\n _pattern?: CanvasPattern\n _pattern_img?: HTMLImageElement\n bg_tint?: string | CanvasGradient | CanvasPattern\n // TODO: This looks like another panel thing\n prompt_box?: PromptDialog | null\n search_box?: HTMLDivElement\n /** @deprecated Panels */\n SELECTED_NODE?: LGraphNode\n /** @deprecated Panels */\n NODEPANEL_IS_OPEN?: boolean\n\n /** Once per frame check of snap to grid value. @todo Update on change. */\n #snapToGrid?: number\n /** Set on keydown, keyup. @todo */\n #shiftDown: boolean = false\n\n /** Link rendering adapter for litegraph-to-canvas integration */\n linkRenderer: LitegraphLinkAdapter | null = null\n\n /** If true, enable drag zoom. Ctrl+Shift+Drag Up/Down: zoom canvas. */\n dragZoomEnabled: boolean = false\n /** The start position of the drag zoom and original read-only state. */\n #dragZoomStart: { pos: Point; scale: number; readOnly: boolean } | null = null\n\n /** If true, enable live selection during drag. Nodes are selected/deselected in real-time. */\n liveSelection: boolean = false\n\n getMenuOptions?(): IContextMenuValue<string>[]\n getExtraMenuOptions?(\n canvas: LGraphCanvas,\n options: (IContextMenuValue<string> | null)[]\n ): (IContextMenuValue<string> | null)[]\n static active_node: LGraphNode\n /** called before modifying the graph */\n onBeforeChange?(graph: LGraph): void\n /** called after modifying the graph */\n onAfterChange?(graph: LGraph): void\n onClear?: () => void\n /** called after moving a node @deprecated Does not handle multi-node move, and can return the wrong node. */\n onNodeMoved?: (node_dragged: LGraphNode | undefined) => void\n /** @deprecated Called with the deprecated {@link selected_nodes} when the selection changes. Replacement not yet impl. */\n onSelectionChange?: (selected: Dictionary<Positionable>) => void\n /** called when rendering a tooltip */\n onDrawLinkTooltip?: (\n ctx: CanvasRenderingContext2D,\n link: LLink | null,\n canvas?: LGraphCanvas\n ) => boolean\n\n /** to render foreground objects not affected by transform (for GUIs) */\n onDrawOverlay?: (ctx: CanvasRenderingContext2D) => void\n onRenderBackground?: (\n canvas: HTMLCanvasElement,\n ctx: CanvasRenderingContext2D\n ) => boolean\n\n onNodeDblClicked?: (n: LGraphNode) => void\n onShowNodePanel?: (n: LGraphNode) => void\n onNodeSelected?: (node: LGraphNode) => void\n onNodeDeselected?: (node: LGraphNode) => void\n onRender?: (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) => void\n\n /**\n * Creates a new instance of LGraphCanvas.\n * @param canvas The canvas HTML element (or its id) to use, or null / undefined to leave blank.\n * @param graph The graph that owns this canvas.\n * @param options\n */\n constructor(\n canvas: HTMLCanvasElement,\n graph: LGraph,\n options?: LGraphCanvas['options']\n ) {\n options ||= {}\n this.options = options\n\n // if(graph === undefined)\n // throw (\"No graph assigned\");\n this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE\n\n this.ds = new DragAndScale(canvas)\n this.pointer = new CanvasPointer(canvas)\n\n // Set up zoom change handler for efficient LOD updates\n this.ds.onChanged = (scale: number, _offset: Point) => {\n // Only check LOD threshold if it's enabled\n if (this._lowQualityZoomThreshold > 0) {\n this._isLowQuality = scale < this._lowQualityZoomThreshold\n }\n }\n\n // Initialize link renderer if graph is available\n if (graph) {\n this.linkRenderer = new LitegraphLinkAdapter(false)\n }\n\n this.linkConnector.events.addEventListener('link-created', () =>\n this.#dirty()\n )\n\n // @deprecated Workaround: Keep until connecting_links is removed.\n this.linkConnector.events.addEventListener('reset', () => {\n this.connecting_links = null\n this.dirty_bgcanvas = true\n })\n\n // Dropped a link on the canvas\n this.linkConnector.events.addEventListener(\n 'dropped-on-canvas',\n (customEvent) => {\n if (!this.connecting_links) return\n\n const e = customEvent.detail\n this.emitEvent({\n subType: 'empty-release',\n originalEvent: e,\n linkReleaseContext: { links: this.connecting_links }\n })\n\n const firstLink = this.linkConnector.renderLinks[0]\n\n // No longer in use\n // add menu when releasing link in empty space\n if (LiteGraph.release_link_on_empty_shows_menu) {\n const linkReleaseContext =\n this.linkConnector.state.connectingTo === 'input'\n ? {\n node_from: firstLink.node as LGraphNode,\n slot_from: firstLink.fromSlot as INodeOutputSlot,\n type_filter_in: firstLink.fromSlot.type\n }\n : {\n node_to: firstLink.node as LGraphNode,\n slot_to: firstLink.fromSlot as INodeInputSlot,\n type_filter_out: firstLink.fromSlot.type\n }\n\n const afterRerouteId = firstLink.fromReroute?.id\n\n if ('shiftKey' in e && e.shiftKey) {\n if (this.allow_searchbox) {\n this.showSearchBox(\n e as unknown as MouseEvent,\n linkReleaseContext as IShowSearchOptions\n )\n }\n } else if (this.linkConnector.state.connectingTo === 'input') {\n this.showConnectionMenu({\n nodeFrom: firstLink.node as LGraphNode,\n slotFrom: firstLink.fromSlot as INodeOutputSlot,\n e,\n afterRerouteId\n })\n } else {\n this.showConnectionMenu({\n nodeTo: firstLink.node as LGraphNode,\n slotTo: firstLink.fromSlot as INodeInputSlot,\n e,\n afterRerouteId\n })\n }\n }\n }\n )\n\n // otherwise it generates ugly patterns when scaling down too much\n this.zoom_modify_alpha = true\n // in range (1.01, 2.5). Less than 1 will invert the zoom direction\n this.zoom_speed = 1.1\n\n this.node_title_color = LiteGraph.NODE_TITLE_COLOR\n this.default_link_color = LiteGraph.LINK_COLOR\n this.default_connection_color = {\n input_off: '#778',\n input_on: '#7F7',\n output_off: '#778',\n output_on: '#7F7'\n }\n this.default_connection_color_byType = {\n /* number: \"#7F7\",\n string: \"#77F\",\n boolean: \"#F77\", */\n }\n this.default_connection_color_byTypeOff = {\n /* number: \"#474\",\n string: \"#447\",\n boolean: \"#744\", */\n }\n\n this.highquality_render = true\n // set to true to render titlebar with gradients\n this.use_gradients = false\n // used for transition\n this.editor_alpha = 1\n this.pause_rendering = false\n this.clear_background = true\n this.clear_background_color = '#222'\n\n this.render_only_selected = true\n this.show_info = true\n this.allow_dragcanvas = true\n this.allow_dragnodes = true\n // allow to control widgets, buttons, collapse, etc\n this.allow_interaction = true\n // allow selecting multi nodes without pressing extra keys\n this.multi_select = false\n this.allow_searchbox = true\n // allows to change a connection with having to redo it again\n this.allow_reconnect_links = true\n // snap to grid\n this.align_to_grid = false\n\n this.drag_mode = false\n this.dragging_rectangle = null\n\n // allows to filter to only accept some type of nodes in a graph\n this.filter = null\n\n // forces to redraw the canvas on mouse events (except move)\n this.set_canvas_dirty_on_mouse_event = true\n this.always_render_background = false\n this.render_shadows = true\n this.render_canvas_border = true\n // too much cpu\n this.render_connections_shadows = false\n this.render_connections_border = true\n this.render_curved_connections = false\n this.render_connection_arrows = false\n this.render_collapsed_slots = true\n this.render_execution_order = false\n this.render_link_tooltip = true\n\n this.links_render_mode = LinkRenderType.SPLINE_LINK\n\n this.mouse = [0, 0]\n this.graph_mouse = [0, 0]\n this.canvas_mouse = this.graph_mouse\n\n this.connections_width = 3\n\n this.current_node = null\n this.node_widget = null\n this.last_mouse_position = [0, 0]\n this.visible_area = this.ds.visible_area\n // Explicitly null-checked\n this.connecting_links = null\n\n // to constraint render area to a portion of the canvas\n this.viewport = options.viewport\n\n // link canvas and graph\n this.graph = graph\n graph?.attachCanvas(this)\n\n // TypeScript strict workaround: cannot use method to initialize properties.\n this.canvas = undefined!\n this.bgcanvas = undefined!\n this.ctx = undefined!\n\n this.setCanvas(canvas, options.skip_events)\n this.clear()\n\n LGraphCanvas._measureText = (\n text: string,\n fontStyle = this.inner_text_font\n ) => {\n const { ctx } = this\n const { font } = ctx\n try {\n ctx.font = fontStyle\n return ctx.measureText(text).width\n } finally {\n ctx.font = font\n }\n }\n\n if (!options.skip_render) {\n this.startRendering()\n }\n\n this.autoresize = options.autoresize ?? false\n\n this.updateLowQualityThreshold()\n }\n\n static onGroupAdd(\n _info: unknown,\n _entry: unknown,\n mouse_event: MouseEvent\n ): void {\n const canvas = LGraphCanvas.active_canvas\n\n const group = new LiteGraph.LGraphGroup()\n group.pos = canvas.convertEventToCanvasOffset(mouse_event)\n if (!canvas.graph) throw new NullGraphError()\n canvas.graph.add(group)\n }\n\n /**\n * @deprecated Functionality moved to {@link getBoundaryNodes}. The new function returns null on failure, instead of an object with all null properties.\n * Determines the furthest nodes in each direction\n * @param nodes the nodes to from which boundary nodes will be extracted\n * @returns\n */\n static getBoundaryNodes(\n nodes: LGraphNode[] | Dictionary<LGraphNode>\n ): NullableProperties<IBoundaryNodes> {\n const _nodes = Array.isArray(nodes) ? nodes : Object.values(nodes)\n return (\n getBoundaryNodes(_nodes) ?? {\n top: null,\n right: null,\n bottom: null,\n left: null\n }\n )\n }\n\n /**\n * @deprecated Functionality moved to {@link alignNodes}. The new function does not set dirty canvas.\n * @param nodes a list of nodes\n * @param direction Direction to align the nodes\n * @param align_to Node to align to (if null, align to the furthest node in the given direction)\n */\n static alignNodes(\n nodes: Dictionary<LGraphNode>,\n direction: Direction,\n align_to?: LGraphNode\n ): void {\n const newPositions = alignNodes(Object.values(nodes), direction, align_to)\n LGraphCanvas.active_canvas.repositionNodesVueMode(newPositions)\n LGraphCanvas.active_canvas.setDirty(true, true)\n }\n\n static onNodeAlign(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n event: MouseEvent,\n prev_menu: ContextMenu<string>,\n node: LGraphNode\n ): void {\n new LiteGraph.ContextMenu(['Top', 'Bottom', 'Left', 'Right'], {\n event,\n callback: inner_clicked,\n parentMenu: prev_menu\n })\n\n function inner_clicked(value: string) {\n const newPositions = alignNodes(\n Object.values(LGraphCanvas.active_canvas.selected_nodes),\n value.toLowerCase() as Direction,\n node\n )\n LGraphCanvas.active_canvas.repositionNodesVueMode(newPositions)\n LGraphCanvas.active_canvas.setDirty(true, true)\n }\n }\n\n static onGroupAlign(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n event: MouseEvent,\n prev_menu: ContextMenu<string>\n ): void {\n new LiteGraph.ContextMenu(['Top', 'Bottom', 'Left', 'Right'], {\n event,\n callback: inner_clicked,\n parentMenu: prev_menu\n })\n\n function inner_clicked(value: string) {\n const newPositions = alignNodes(\n Object.values(LGraphCanvas.active_canvas.selected_nodes),\n value.toLowerCase() as Direction\n )\n LGraphCanvas.active_canvas.repositionNodesVueMode(newPositions)\n LGraphCanvas.active_canvas.setDirty(true, true)\n }\n }\n\n static createDistributeMenu(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n event: MouseEvent,\n prev_menu: ContextMenu<string>\n ): void {\n new LiteGraph.ContextMenu(['Vertically', 'Horizontally'], {\n event,\n callback: inner_clicked,\n parentMenu: prev_menu\n })\n\n function inner_clicked(value: string) {\n const canvas = LGraphCanvas.active_canvas\n const newPositions = distributeNodes(\n Object.values(canvas.selected_nodes),\n value === 'Horizontally'\n )\n canvas.repositionNodesVueMode(newPositions)\n canvas.setDirty(true, true)\n }\n }\n\n static onMenuAdd(\n _value: unknown,\n _options: unknown,\n e: MouseEvent,\n prev_menu?: ContextMenu<string>,\n callback?: (node: LGraphNode | null) => void\n ): boolean | undefined {\n const canvas = LGraphCanvas.active_canvas\n const { graph } = canvas\n if (!graph) return\n\n inner_onMenuAdded('', prev_menu)\n return false\n\n type AddNodeMenu = Omit<IContextMenuValue<string>, 'callback'> & {\n callback: (\n value: { value: string },\n event: Event,\n mouseEvent: MouseEvent,\n contextMenu: ContextMenu<string>\n ) => void\n }\n\n function inner_onMenuAdded(\n base_category: string,\n prev_menu?: ContextMenu<string>\n ): void {\n if (!graph) return\n\n const categories = LiteGraph.getNodeTypesCategories(\n canvas.filter || graph.filter\n ).filter((category) => category.startsWith(base_category))\n const entries: AddNodeMenu[] = []\n\n for (const category of categories) {\n if (!category) continue\n\n const base_category_regex = new RegExp(`^(${base_category})`)\n const category_name = category\n .replace(base_category_regex, '')\n .split('/', 1)[0]\n const category_path =\n base_category === ''\n ? `${category_name}/`\n : `${base_category}${category_name}/`\n\n let name = category_name\n // in case it has a namespace like \"shader::math/rand\" it hides the namespace\n if (name.includes('::')) name = name.split('::', 2)[1]\n\n const index = entries.findIndex(\n (entry) => entry.value === category_path\n )\n if (index === -1) {\n entries.push({\n value: category_path,\n content: name,\n has_submenu: true,\n callback: function (value, _event, _mouseEvent, contextMenu) {\n inner_onMenuAdded(value.value, contextMenu)\n }\n })\n }\n }\n\n const nodes = LiteGraph.getNodeTypesInCategory(\n base_category.slice(0, -1),\n canvas.filter || graph.filter\n )\n\n for (const node of nodes) {\n if (node.skip_list) continue\n\n const entry: AddNodeMenu = {\n value: node.type,\n content: node.title,\n has_submenu: false,\n callback: function (value, _event, _mouseEvent, contextMenu) {\n if (!canvas.graph) throw new NullGraphError()\n\n const first_event = contextMenu.getFirstEvent()\n canvas.graph.beforeChange()\n const node = LiteGraph.createNode(value.value)\n if (node) {\n if (!first_event)\n throw new TypeError(\n 'Context menu event was null. This should not occur in normal usage.'\n )\n node.pos = canvas.convertEventToCanvasOffset(first_event)\n canvas.graph.add(node)\n } else {\n console.warn('Failed to create node of type:', value.value)\n }\n\n callback?.(node)\n canvas.graph.afterChange()\n }\n }\n\n entries.push(entry)\n }\n\n new LiteGraph.ContextMenu(entries, { event: e, parentMenu: prev_menu })\n }\n }\n\n static onMenuCollapseAll() {}\n static onMenuNodeEdit() {}\n\n /** @param _options Parameter is never used */\n static showMenuNodeOptionalOutputs(\n _v: unknown,\n /** Unused - immediately overwritten */\n _options: INodeOutputSlot[],\n e: MouseEvent,\n prev_menu: ContextMenu<INodeSlotContextItem>,\n node: LGraphNode\n ): boolean | undefined {\n if (!node) return\n\n const canvas = LGraphCanvas.active_canvas\n\n let entries: (IContextMenuValue<INodeSlotContextItem> | null)[] = []\n\n if (\n LiteGraph.do_add_triggers_slots &&\n node.findOutputSlot('onExecuted') == -1\n ) {\n entries.push({\n content: 'On Executed',\n value: ['onExecuted', LiteGraph.EVENT, { nameLocked: true }],\n className: 'event'\n })\n }\n // add callback for modifying the menu elements onMenuNodeOutputs\n const retEntries = node.onMenuNodeOutputs?.(entries)\n if (retEntries) entries = retEntries\n\n if (!entries.length) return\n\n new LiteGraph.ContextMenu<INodeSlotContextItem>(entries, {\n event: e,\n callback: inner_clicked,\n parentMenu: prev_menu,\n node\n })\n\n function inner_clicked(\n this: ContextMenuDivElement<INodeSlotContextItem>,\n v?: string | IContextMenuValue<INodeSlotContextItem>,\n _options?: unknown,\n e?: MouseEvent,\n prev?: ContextMenu<INodeSlotContextItem>\n ) {\n if (!node) return\n if (!v || typeof v === 'string') return\n\n // TODO: This is a static method, so the below \"that\" appears broken.\n if (v.callback) void v.callback.call(this, node, v, e, prev)\n\n if (!v.value) return\n\n const value = v.value[1]\n\n if (value && (typeof value === 'object' || Array.isArray(value))) {\n // submenu why?\n const entries = []\n for (const i in value) {\n entries.push({ content: i, value: value[i] })\n }\n new LiteGraph.ContextMenu(entries, {\n event: e,\n callback: inner_clicked,\n parentMenu: prev_menu,\n node\n })\n return false\n }\n\n const { graph } = node\n if (!graph) throw new NullGraphError()\n\n graph.beforeChange()\n node.addOutput(v.value[0], v.value[1], v.value[2])\n\n // a callback to the node when adding a slot\n node.onNodeOutputAdd?.(v.value)\n canvas.setDirty(true, true)\n graph.afterChange()\n }\n\n return false\n }\n\n /** @param value Parameter is never used */\n static onShowMenuNodeProperties(\n value: NodeProperty | undefined,\n _options: unknown,\n e: MouseEvent,\n prev_menu: ContextMenu<string>,\n node: LGraphNode\n ): boolean | undefined {\n if (!node || !node.properties) return\n\n const canvas = LGraphCanvas.active_canvas\n\n const entries: IContextMenuValue<string>[] = []\n for (const i in node.properties) {\n value = node.properties[i] !== undefined ? node.properties[i] : ' '\n if (typeof value == 'object') value = JSON.stringify(value)\n const info = node.getPropertyInfo(i)\n if (info.type == 'enum' || info.type == 'combo')\n value = LGraphCanvas.getPropertyPrintableValue(value, info.values)\n\n // value could contain invalid html characters, clean that\n value = LGraphCanvas.decodeHTML(toString(value))\n entries.push({\n content:\n `<span class='property_name'>${info.label || i}</span>` +\n `<span class='property_value'>${value}</span>`,\n value: i\n })\n }\n if (!entries.length) {\n return\n }\n\n new LiteGraph.ContextMenu<string>(entries, {\n event: e,\n callback: inner_clicked,\n parentMenu: prev_menu,\n node\n })\n\n function inner_clicked(\n this: ContextMenu<string>,\n v?: string | IContextMenuValue<string>\n ) {\n if (!node || typeof v === 'string' || !v?.value) return\n\n const rect = this.root.getBoundingClientRect()\n canvas.showEditPropertyValue(node, v.value, {\n position: [rect.left, rect.top]\n })\n }\n\n return false\n }\n\n /** @deprecated */\n static decodeHTML(str: string): string {\n const e = document.createElement('div')\n e.textContent = str\n return e.innerHTML\n }\n\n static onMenuResizeNode(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n _e: MouseEvent,\n _menu: ContextMenu,\n node: LGraphNode\n ): void {\n if (!node) return\n\n const fApplyMultiNode = function (node: LGraphNode) {\n node.setSize(node.computeSize())\n }\n\n const canvas = LGraphCanvas.active_canvas\n if (\n !canvas.selected_nodes ||\n Object.keys(canvas.selected_nodes).length <= 1\n ) {\n fApplyMultiNode(node)\n } else {\n for (const i in canvas.selected_nodes) {\n fApplyMultiNode(canvas.selected_nodes[i])\n }\n }\n\n canvas.setDirty(true, true)\n }\n\n // TODO refactor :: this is used fot title but not for properties!\n static onShowPropertyEditor(\n item: { property: keyof LGraphNode; type: string },\n _options: IContextMenuOptions<string>,\n e: MouseEvent,\n _menu: ContextMenu<string>,\n node: LGraphNode\n ): void {\n const property = item.property || 'title'\n const value = node[property]\n\n const title = document.createElement('span')\n title.className = 'name'\n title.textContent = property\n\n const input = document.createElement('input')\n Object.assign(input, { type: 'text', className: 'value', autofocus: true })\n\n const button = document.createElement('button')\n button.textContent = 'OK'\n\n // TODO refactor :: use createDialog ?\n const dialog = Object.assign(document.createElement('div'), {\n is_modified: false,\n className: 'graphdialog',\n close: () => dialog.remove()\n })\n dialog.append(title, input, button)\n\n input.value = String(value)\n input.addEventListener('blur', function () {\n this.focus()\n })\n input.addEventListener('keydown', (e: KeyboardEvent) => {\n dialog.is_modified = true\n if (e.key == 'Escape') {\n // ESC\n dialog.close()\n } else if (e.key == 'Enter') {\n // save\n inner()\n } else if (\n !e.target ||\n !('localName' in e.target) ||\n e.target.localName != 'textarea'\n ) {\n return\n }\n e.preventDefault()\n e.stopPropagation()\n })\n\n const canvas = LGraphCanvas.active_canvas\n const canvasEl = canvas.canvas\n\n const rect = canvasEl.getBoundingClientRect()\n const offsetx = rect ? -20 - rect.left : -20\n const offsety = rect ? -20 - rect.top : -20\n\n if (e) {\n dialog.style.left = `${e.clientX + offsetx}px`\n dialog.style.top = `${e.clientY + offsety}px`\n } else {\n dialog.style.left = `${canvasEl.width * 0.5 + offsetx}px`\n dialog.style.top = `${canvasEl.height * 0.5 + offsety}px`\n }\n\n button.addEventListener('click', inner)\n\n if (canvasEl.parentNode == null)\n throw new TypeError('canvasEl.parentNode was null')\n canvasEl.parentNode.append(dialog)\n\n input.focus()\n\n let dialogCloseTimer: ReturnType<typeof setTimeout> | undefined\n dialog.addEventListener('mouseleave', function () {\n if (LiteGraph.dialog_close_on_mouse_leave) {\n if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) {\n dialogCloseTimer = setTimeout(\n dialog.close,\n LiteGraph.dialog_close_on_mouse_leave_delay\n )\n }\n }\n })\n dialog.addEventListener('mouseenter', function () {\n if (LiteGraph.dialog_close_on_mouse_leave) {\n if (dialogCloseTimer) clearTimeout(dialogCloseTimer)\n }\n })\n\n function inner() {\n if (input) setValue(input.value)\n }\n\n function setValue(value: NodeProperty) {\n if (item.type == 'Number') {\n value = Number(value)\n } else if (item.type == 'Boolean') {\n value = Boolean(value)\n }\n // @ts-expect-error Requires refactor.\n node[property] = value\n dialog.remove()\n canvas.setDirty(true, true)\n }\n }\n\n static getPropertyPrintableValue(\n value: unknown,\n values: unknown[] | object | undefined\n ): string | undefined {\n if (!values) return String(value)\n\n if (Array.isArray(values)) {\n return String(value)\n }\n\n if (typeof values === 'object') {\n let desc_value = ''\n for (const k in values) {\n // @ts-expect-error deprecated #578\n if (values[k] != value) continue\n\n desc_value = k\n break\n }\n return `${String(value)} (${desc_value})`\n }\n }\n\n static onMenuNodeCollapse(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n _e: MouseEvent,\n _menu: ContextMenu,\n node: LGraphNode\n ): void {\n if (!node.graph) throw new NullGraphError()\n\n node.graph.beforeChange()\n\n const fApplyMultiNode = function (node: LGraphNode) {\n node.collapse()\n }\n\n const graphcanvas = LGraphCanvas.active_canvas\n if (\n !graphcanvas.selected_nodes ||\n Object.keys(graphcanvas.selected_nodes).length <= 1\n ) {\n fApplyMultiNode(node)\n } else {\n for (const i in graphcanvas.selected_nodes) {\n fApplyMultiNode(graphcanvas.selected_nodes[i])\n }\n }\n\n node.graph.afterChange()\n }\n\n static onMenuToggleAdvanced(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n _e: MouseEvent,\n _menu: ContextMenu,\n node: LGraphNode\n ): void {\n if (!node.graph) throw new NullGraphError()\n\n node.graph.beforeChange()\n const fApplyMultiNode = function (node: LGraphNode) {\n node.toggleAdvanced()\n }\n\n const graphcanvas = LGraphCanvas.active_canvas\n if (\n !graphcanvas.selected_nodes ||\n Object.keys(graphcanvas.selected_nodes).length <= 1\n ) {\n fApplyMultiNode(node)\n } else {\n for (const i in graphcanvas.selected_nodes) {\n fApplyMultiNode(graphcanvas.selected_nodes[i])\n }\n }\n node.graph.afterChange()\n }\n\n static onMenuNodeMode(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n e: MouseEvent,\n menu: ContextMenu,\n node: LGraphNode\n ): boolean {\n new LiteGraph.ContextMenu(LiteGraph.NODE_MODES, {\n event: e,\n callback: inner_clicked,\n parentMenu: menu,\n node\n })\n\n function inner_clicked(v: string) {\n if (!node) return\n\n const kV = Object.values(LiteGraph.NODE_MODES).indexOf(v)\n const fApplyMultiNode = function (node: LGraphNode) {\n if (kV !== -1 && LiteGraph.NODE_MODES[kV]) {\n node.changeMode(kV)\n } else {\n console.warn(`unexpected mode: ${v}`)\n node.changeMode(LGraphEventMode.ALWAYS)\n }\n }\n\n const graphcanvas = LGraphCanvas.active_canvas\n if (\n !graphcanvas.selected_nodes ||\n Object.keys(graphcanvas.selected_nodes).length <= 1\n ) {\n fApplyMultiNode(node)\n } else {\n for (const i in graphcanvas.selected_nodes) {\n fApplyMultiNode(graphcanvas.selected_nodes[i])\n }\n }\n }\n\n return false\n }\n\n /** @param value Parameter is never used */\n static onMenuNodeColors(\n value: IContextMenuValue<string | null>,\n _options: IContextMenuOptions,\n e: MouseEvent,\n menu: ContextMenu<string | null>,\n node: LGraphNode\n ): boolean {\n if (!node) throw 'no node for color'\n\n const values: IContextMenuValue<\n string | null,\n unknown,\n { value: string | null }\n >[] = []\n values.push({\n value: null,\n content:\n \"<span style='display: block; padding-left: 4px;'>No color</span>\"\n })\n\n for (const i in LGraphCanvas.node_colors) {\n const color = LGraphCanvas.node_colors[i]\n value = {\n value: i,\n content:\n `<span style='display: block; color: #999; padding-left: 4px;` +\n ` border-left: 8px solid ${color.color}; background-color:${color.bgcolor}'>${i}</span>`\n }\n values.push(value)\n }\n new LiteGraph.ContextMenu<string | null>(values, {\n event: e,\n callback: inner_clicked,\n parentMenu: menu,\n node\n })\n\n function inner_clicked(v: IContextMenuValue<string>) {\n if (!node) return\n\n const fApplyColor = function (item: IColorable) {\n const colorOption = v.value ? LGraphCanvas.node_colors[v.value] : null\n item.setColorOption(colorOption)\n }\n\n const canvas = LGraphCanvas.active_canvas\n if (\n !canvas.selected_nodes ||\n Object.keys(canvas.selected_nodes).length <= 1\n ) {\n fApplyColor(node)\n } else {\n for (const i in canvas.selected_nodes) {\n fApplyColor(canvas.selected_nodes[i])\n }\n }\n canvas.setDirty(true, true)\n }\n\n return false\n }\n\n static onMenuNodeShapes(\n _value: IContextMenuValue<(typeof LiteGraph.VALID_SHAPES)[number]>,\n _options: IContextMenuOptions<(typeof LiteGraph.VALID_SHAPES)[number]>,\n e: MouseEvent,\n menu?: ContextMenu<(typeof LiteGraph.VALID_SHAPES)[number]>,\n node?: LGraphNode\n ): boolean {\n if (!node) throw 'no node passed'\n\n new LiteGraph.ContextMenu<(typeof LiteGraph.VALID_SHAPES)[number]>(\n LiteGraph.VALID_SHAPES,\n {\n event: e,\n callback: inner_clicked,\n parentMenu: menu,\n node\n }\n )\n\n function inner_clicked(v: (typeof LiteGraph.VALID_SHAPES)[number]) {\n if (!node) return\n if (!node.graph) throw new NullGraphError()\n\n node.graph.beforeChange()\n\n const fApplyMultiNode = function (node: LGraphNode) {\n node.shape = v\n }\n\n const canvas = LGraphCanvas.active_canvas\n if (\n !canvas.selected_nodes ||\n Object.keys(canvas.selected_nodes).length <= 1\n ) {\n fApplyMultiNode(node)\n } else {\n for (const i in canvas.selected_nodes) {\n fApplyMultiNode(canvas.selected_nodes[i])\n }\n }\n\n node.graph.afterChange()\n canvas.setDirty(true)\n }\n\n return false\n }\n\n static onMenuNodeRemove(): void {\n LGraphCanvas.active_canvas.deleteSelected()\n }\n\n static onMenuNodeClone(\n _value: IContextMenuValue,\n _options: IContextMenuOptions,\n _e: MouseEvent,\n _menu: ContextMenu,\n node: LGraphNode\n ): void {\n const canvas = LGraphCanvas.active_canvas\n const nodes = canvas.selectedItems.size ? [...canvas.selectedItems] : [node]\n if (nodes.length) LGraphCanvas.cloneNodes(nodes)\n }\n\n static cloneNodes(nodes: Positionable[]) {\n const canvas = LGraphCanvas.active_canvas\n\n // Find top-left-most boundary\n let offsetX = Infinity\n let offsetY = Infinity\n for (const item of nodes) {\n if (item.pos == null)\n throw new TypeError(\n 'Invalid node encountered on clone. `pos` was null.'\n )\n offsetX = Math.min(offsetX, item.pos[0])\n offsetY = Math.min(offsetY, item.pos[1])\n }\n\n return canvas._deserializeItems(canvas._serializeItems(nodes), {\n position: [offsetX + 5, offsetY + 5]\n })\n }\n\n /**\n * clears all the data inside\n *\n */\n clear(): void {\n this.frame = 0\n this.last_draw_time = 0\n this.render_time = 0\n this.fps = 0\n\n // this.scale = 1;\n // this.offset = [0,0];\n this.dragging_rectangle = null\n\n this.selected_nodes = {}\n this.selected_group = null\n this.selectedItems.clear()\n this.state.selectionChanged = true\n this.onSelectionChange?.(this.selected_nodes)\n\n this.visible_nodes = []\n this.node_over = undefined\n this.node_capturing_input = null\n this.connecting_links = null\n this.highlighted_links = {}\n\n this.dragging_canvas = false\n\n this.#dirty()\n this.dirty_area = null\n\n this.node_in_panel = null\n this.node_widget = null\n\n this.last_mouse = [0, 0]\n this.last_mouseclick = 0\n this.pointer.reset()\n this.visible_area.set([0, 0, 0, 0])\n\n this.onClear?.()\n }\n\n /**\n * Assigns a new graph to this canvas.\n */\n setGraph(newGraph: LGraph | Subgraph): void {\n const { graph } = this\n if (newGraph === graph) return\n\n this.clear()\n newGraph.attachCanvas(this)\n\n // Re-initialize link renderer with new graph\n this.linkRenderer = new LitegraphLinkAdapter(false)\n\n this.dispatch('litegraph:set-graph', { newGraph, oldGraph: graph })\n this.#dirty()\n }\n\n openSubgraph(subgraph: Subgraph, fromNode: SubgraphNode): void {\n const { graph } = this\n if (!graph) throw new NullGraphError()\n\n const options = {\n bubbles: true,\n detail: { subgraph, closingGraph: graph, fromNode },\n cancelable: true\n }\n const mayContinue = this.canvas.dispatchEvent(\n new CustomEvent('subgraph-opening', options)\n )\n if (!mayContinue) return\n\n this.clear()\n this.subgraph = subgraph\n this.setGraph(subgraph)\n\n this.canvas.dispatchEvent(new CustomEvent('subgraph-opened', options))\n }\n\n /**\n * @returns the visually active graph (in case there are more in the stack)\n */\n getCurrentGraph(): LGraph | null {\n return this.graph\n }\n\n /**\n * Finds the canvas if required, throwing on failure.\n * @param canvas Canvas element, or its element ID\n * @returns The canvas element\n * @throws If {@link canvas} is an element ID that does not belong to a valid HTML canvas element\n */\n #validateCanvas(\n canvas: string | HTMLCanvasElement\n ): HTMLCanvasElement & { data?: LGraphCanvas } {\n if (typeof canvas === 'string') {\n const el = document.getElementById(canvas)\n if (!(el instanceof HTMLCanvasElement))\n throw 'Error validating LiteGraph canvas: Canvas element not found'\n return el\n }\n return canvas\n }\n\n /**\n * Sets the current HTML canvas element.\n * Calls bindEvents to add input event listeners, and (re)creates the background canvas.\n * @param canvas The canvas element to assign, or its HTML element ID. If null or undefined, the current reference is cleared.\n * @param skip_events If true, events on the previous canvas will not be removed. Has no effect on the first invocation.\n */\n setCanvas(canvas: string | HTMLCanvasElement, skip_events?: boolean) {\n const element = this.#validateCanvas(canvas)\n if (element === this.canvas) return\n // maybe detach events from old_canvas\n if (!element && this.canvas && !skip_events) this.unbindEvents()\n\n this.canvas = element\n this.ds.element = element\n this.pointer.element = element\n\n if (!element) return\n\n // TODO: classList.add\n element.className += ' lgraphcanvas'\n element.data = this\n\n // Background canvas: To render objects behind nodes (background, links, groups)\n this.bgcanvas = document.createElement('canvas')\n this.bgcanvas.width = this.canvas.width\n this.bgcanvas.height = this.canvas.height\n\n const ctx = element.getContext?.('2d')\n if (ctx == null) {\n if (element.localName != 'canvas') {\n throw `Element supplied for LGraphCanvas must be a <canvas> element, you passed a ${element.localName}`\n }\n throw \"This browser doesn't support Canvas\"\n }\n this.ctx = ctx\n\n if (!skip_events) this.bindEvents()\n }\n\n /** Captures an event and prevents default - returns false. */\n _doNothing(e: Event): boolean {\n // console.log(\"pointerevents: _doNothing \"+e.type);\n e.preventDefault()\n return false\n }\n\n /** Captures an event and prevents default - returns true. */\n _doReturnTrue(e: Event): boolean {\n e.preventDefault()\n return true\n }\n\n /**\n * binds mouse, keyboard, touch and drag events to the canvas\n */\n bindEvents(): void {\n if (this._events_binded) {\n console.warn('LGraphCanvas: events already bound')\n return\n }\n\n const { canvas } = this\n // hack used when moving canvas between windows\n const { document } = this.getCanvasWindow()\n\n this._mousedown_callback = this.processMouseDown.bind(this)\n this._mousewheel_callback = this.processMouseWheel.bind(this)\n this._mousemove_callback = this.processMouseMove.bind(this)\n this._mouseup_callback = this.processMouseUp.bind(this)\n this._mouseout_callback = this.processMouseOut.bind(this)\n this._mousecancel_callback = this.processMouseCancel.bind(this)\n\n canvas.addEventListener('pointerdown', this._mousedown_callback, true)\n canvas.addEventListener('wheel', this._mousewheel_callback, false)\n\n canvas.addEventListener('pointerup', this._mouseup_callback, true)\n canvas.addEventListener('pointermove', this._mousemove_callback)\n canvas.addEventListener('pointerout', this._mouseout_callback)\n canvas.addEventListener('pointercancel', this._mousecancel_callback, true)\n\n canvas.addEventListener('contextmenu', this._doNothing)\n\n // Keyboard\n this._key_callback = this.processKey.bind(this)\n\n canvas.addEventListener('keydown', this._key_callback, true)\n // keyup event must be bound on the document\n document.addEventListener('keyup', this._key_callback, true)\n\n canvas.addEventListener('dragover', this._doNothing, false)\n canvas.addEventListener('dragend', this._doNothing, false)\n canvas.addEventListener('dragenter', this._doReturnTrue, false)\n\n this._events_binded = true\n }\n\n /**\n * unbinds mouse events from the canvas\n */\n unbindEvents(): void {\n if (!this._events_binded) {\n console.warn('LGraphCanvas: no events bound')\n return\n }\n\n // console.log(\"pointerevents: unbindEvents\");\n const { document } = this.getCanvasWindow()\n const { canvas } = this\n\n // Assertions: removing nullish is fine.\n canvas.removeEventListener('pointercancel', this._mousecancel_callback!)\n canvas.removeEventListener('pointerout', this._mouseout_callback!)\n canvas.removeEventListener('pointermove', this._mousemove_callback!)\n canvas.removeEventListener('pointerup', this._mouseup_callback!)\n canvas.removeEventListener('pointerdown', this._mousedown_callback!)\n canvas.removeEventListener('wheel', this._mousewheel_callback!)\n canvas.removeEventListener('keydown', this._key_callback!)\n document.removeEventListener('keyup', this._key_callback!)\n canvas.removeEventListener('contextmenu', this._doNothing)\n canvas.removeEventListener('dragenter', this._doReturnTrue)\n\n this._mousedown_callback = undefined\n this._mousewheel_callback = undefined\n this._key_callback = undefined\n\n this._events_binded = false\n }\n\n /**\n * Ensures the canvas will be redrawn on the next frame by setting the dirty flag(s).\n * Without parameters, this function does nothing.\n * @todo Impl. `setDirty()` or similar as shorthand to redraw everything.\n * @param fgcanvas If true, marks the foreground canvas as dirty (nodes and anything drawn on top of them). Default: false\n * @param bgcanvas If true, mark the background canvas as dirty (background, groups, links). Default: false\n */\n setDirty(fgcanvas: boolean, bgcanvas?: boolean): void {\n if (fgcanvas) this.dirty_canvas = true\n if (bgcanvas) this.dirty_bgcanvas = true\n }\n\n /** Marks the entire canvas as dirty. */\n #dirty(): void {\n this.dirty_canvas = true\n this.dirty_bgcanvas = true\n }\n\n #linkConnectorDrop(): void {\n const { graph, linkConnector, pointer } = this\n if (!graph) throw new NullGraphError()\n\n pointer.onDragEnd = (upEvent) => linkConnector.dropLinks(graph, upEvent)\n pointer.finally = () => this.linkConnector.reset(true)\n }\n\n /**\n * Used to attach the canvas in a popup\n * @returns returns the window where the canvas is attached (the DOM root node)\n */\n getCanvasWindow(): Window {\n if (!this.canvas) return window\n\n const doc = this.canvas.ownerDocument\n // @ts-expect-error Check if required\n return doc.defaultView || doc.parentWindow\n }\n\n /**\n * starts rendering the content of the canvas when needed\n *\n */\n startRendering(): void {\n // already rendering\n if (this.is_rendering) return\n\n this.is_rendering = true\n renderFrame.call(this)\n\n /** Render loop */\n function renderFrame(this: LGraphCanvas) {\n if (!this.pause_rendering) {\n this.draw()\n }\n\n const window = this.getCanvasWindow()\n if (this.is_rendering) {\n if (this.#maximumFrameGap > 0) {\n // Manual FPS limit\n const gap =\n this.#maximumFrameGap - (LiteGraph.getTime() - this.last_draw_time)\n setTimeout(renderFrame.bind(this), Math.max(1, gap))\n } else {\n // FPS limited by refresh rate\n window.requestAnimationFrame(renderFrame.bind(this))\n }\n }\n }\n }\n\n /**\n * stops rendering the content of the canvas (to save resources)\n *\n */\n stopRendering(): void {\n this.is_rendering = false\n /*\n if(this.rendering_timer_id)\n {\n clearInterval(this.rendering_timer_id);\n this.rendering_timer_id = null;\n }\n */\n }\n\n /* LiteGraphCanvas input */\n // used to block future mouse events (because of im gui)\n blockClick(): void {\n this.block_click = true\n this.last_mouseclick = 0\n }\n\n /**\n * Gets the widget at the current cursor position.\n * @param node Optional node to check for widgets under cursor\n * @returns The widget located at the current cursor position, if any is found.\n * @deprecated Use {@link LGraphNode.getWidgetOnPos} instead.\n * ```ts\n * const [x, y] = canvas.graph_mouse\n * const widget = canvas.node_over?.getWidgetOnPos(x, y, true)\n * ```\n */\n getWidgetAtCursor(node?: LGraphNode): IBaseWidget | undefined {\n node ??= this.node_over\n return node?.getWidgetOnPos(this.graph_mouse[0], this.graph_mouse[1], true)\n }\n\n /**\n * Clears highlight and mouse-over information from nodes that should not have it.\n *\n * Intended to be called when the pointer moves away from a node.\n * @param node The node that the mouse is now over\n * @param e MouseEvent that is triggering this\n */\n updateMouseOverNodes(node: LGraphNode | null, e: CanvasPointerEvent): void {\n if (!this.graph) throw new NullGraphError()\n\n const { pointer } = this\n const nodes = this.graph._nodes\n for (const otherNode of nodes) {\n if (otherNode.mouseOver && node != otherNode) {\n // mouse leave\n if (!pointer.eDown) pointer.resizeDirection = undefined\n otherNode.mouseOver = undefined\n this._highlight_input = undefined\n this._highlight_pos = undefined\n this.linkConnector.overWidget = undefined\n\n // Hover transitions\n // TODO: Implement single lerp ease factor for current progress on hover in/out.\n // In drawNode, multiply by ease factor and differential value (e.g. bg alpha +0.5).\n otherNode.lostFocusAt = LiteGraph.getTime()\n\n this.node_over?.onMouseLeave?.(e)\n this.node_over = undefined\n this.dirty_canvas = true\n }\n }\n }\n\n processMouseDown(e: MouseEvent): void {\n if (\n this.dragZoomEnabled &&\n e.ctrlKey &&\n e.shiftKey &&\n !e.altKey &&\n e.buttons\n ) {\n this.#dragZoomStart = {\n pos: [e.x, e.y],\n scale: this.ds.scale,\n readOnly: this.read_only\n }\n this.read_only = true\n return\n }\n\n const { graph, pointer } = this\n this.adjustMouseEvent(e)\n if (e.isPrimary) pointer.down(e)\n\n if (this.set_canvas_dirty_on_mouse_event) this.dirty_canvas = true\n\n if (!graph) return\n\n const ref_window = this.getCanvasWindow()\n LGraphCanvas.active_canvas = this\n\n const x = e.clientX\n const y = e.clientY\n this.ds.viewport = this.viewport\n const is_inside = !this.viewport || isInRect(x, y, this.viewport)\n\n if (!is_inside) return\n\n const node =\n graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes) ?? undefined\n\n this.mouse[0] = x\n this.mouse[1] = y\n this.graph_mouse[0] = e.canvasX\n this.graph_mouse[1] = e.canvasY\n this.last_click_position = [this.mouse[0], this.mouse[1]]\n\n pointer.isDouble = pointer.isDown && e.isPrimary\n pointer.isDown = true\n\n this.canvas.focus()\n\n LiteGraph.closeAllContextMenus(ref_window)\n\n if (this.onMouse?.(e) == true) return\n\n // left button mouse / single finger\n if (e.button === 0 && !pointer.isDouble) {\n this.#processPrimaryButton(e, node)\n } else if (e.button === 1) {\n this.#processMiddleButton(e, node)\n } else if (\n (e.button === 2 || pointer.isDouble) &&\n this.allow_interaction &&\n !this.read_only\n ) {\n // Right / aux button\n const { linkConnector, subgraph } = this\n\n // Sticky select - won't remove single nodes\n if (subgraph?.inputNode.containsPoint(this.graph_mouse)) {\n // Subgraph input node\n this.processSelect(subgraph.inputNode, e, true)\n subgraph.inputNode.onPointerDown(e, pointer, linkConnector)\n } else if (subgraph?.outputNode.containsPoint(this.graph_mouse)) {\n // Subgraph output node\n this.processSelect(subgraph.outputNode, e, true)\n subgraph.outputNode.onPointerDown(e, pointer, linkConnector)\n } else {\n if (node) {\n this.processSelect(node, e, true)\n } else if (this.links_render_mode !== LinkRenderType.HIDDEN_LINK) {\n // Reroutes\n // Try layout store first, fallback to old method\n const rerouteLayout = layoutStore.queryRerouteAtPoint({\n x: e.canvasX,\n y: e.canvasY\n })\n\n let reroute: Reroute | undefined\n if (rerouteLayout) {\n reroute = graph.getReroute(rerouteLayout.id)\n } else {\n reroute = graph.getRerouteOnPos(\n e.canvasX,\n e.canvasY,\n this.#visibleReroutes\n )\n }\n if (reroute) {\n if (e.altKey) {\n pointer.onClick = (upEvent) => {\n if (upEvent.altKey) {\n // Ensure deselected\n if (reroute.selected) {\n this.deselect(reroute)\n this.onSelectionChange?.(this.selected_nodes)\n }\n reroute.remove()\n }\n }\n } else {\n this.processSelect(reroute, e, true)\n }\n }\n }\n\n // Show context menu for the node or group under the pointer\n pointer.onClick ??= () => this.processContextMenu(node, e)\n }\n }\n\n this.last_mouse = [x, y]\n this.last_mouseclick = LiteGraph.getTime()\n this.last_mouse_dragging = true\n\n graph.change()\n\n // this is to ensure to defocus(blur) if a text input element is on focus\n if (\n !ref_window.document.activeElement ||\n (ref_window.document.activeElement.nodeName.toLowerCase() != 'input' &&\n ref_window.document.activeElement.nodeName.toLowerCase() != 'textarea')\n ) {\n e.preventDefault()\n }\n e.stopPropagation()\n\n this.onMouseDown?.(e)\n }\n\n /**\n * Returns the first matching positionable item at the given co-ordinates.\n *\n * Order of preference:\n * - Subgraph IO Nodes\n * - Reroutes\n * - Group titlebars\n * @param x The x coordinate in canvas space\n * @param y The y coordinate in canvas space\n * @returns The positionable item or undefined\n */\n #getPositionableOnPos(x: number, y: number): Positionable | undefined {\n const ioNode = this.subgraph?.getIoNodeOnPos(x, y)\n if (ioNode) return ioNode\n\n for (const reroute of this.#visibleReroutes) {\n if (reroute.containsPoint([x, y])) return reroute\n }\n\n return this.graph?.getGroupTitlebarOnPos(x, y)\n }\n\n #processPrimaryButton(e: CanvasPointerEvent, node: LGraphNode | undefined) {\n const { pointer, graph, linkConnector, subgraph } = this\n if (!graph) throw new NullGraphError()\n\n const x = e.canvasX\n const y = e.canvasY\n\n // Modifiers\n const ctrlOrMeta = e.ctrlKey || e.metaKey\n\n // Multi-select drag rectangle\n if (\n ctrlOrMeta &&\n !e.altKey &&\n LiteGraph.leftMouseClickBehavior === 'panning'\n ) {\n this.#setupNodeSelectionDrag(e, pointer, node)\n\n return\n }\n\n if (this.read_only) {\n pointer.finally = () => (this.dragging_canvas = false)\n this.dragging_canvas = true\n return\n }\n\n // clone node ALT dragging\n if (\n !LiteGraph.vueNodesMode &&\n LiteGraph.alt_drag_do_clone_nodes &&\n e.altKey &&\n !e.ctrlKey &&\n node &&\n this.allow_interaction\n ) {\n const items = this._deserializeItems(this._serializeItems([node]), {\n position: node.pos\n })\n const cloned = items?.created[0] as LGraphNode | undefined\n if (!cloned) return\n\n cloned.pos[0] += 5\n cloned.pos[1] += 5\n\n if (this.allow_dragnodes) {\n pointer.onDragStart = (pointer) => {\n this.#startDraggingItems(cloned, pointer)\n }\n pointer.onDragEnd = (e) => this.#processDraggedItems(e)\n }\n return\n }\n\n // Node clicked\n if (node && (this.allow_interaction || node.flags.allow_interaction)) {\n this.#processNodeClick(e, ctrlOrMeta, node)\n } else {\n // Subgraph IO nodes\n if (subgraph) {\n const { inputNode, outputNode } = subgraph\n\n if (processSubgraphIONode(this, inputNode)) return\n if (processSubgraphIONode(this, outputNode)) return\n\n function processSubgraphIONode(\n canvas: LGraphCanvas,\n ioNode: SubgraphInputNode | SubgraphOutputNode\n ) {\n if (!ioNode.containsPoint([x, y])) return false\n\n ioNode.onPointerDown(e, pointer, linkConnector)\n pointer.onClick ??= () => canvas.processSelect(ioNode, e)\n pointer.onDragStart ??= () =>\n canvas.#startDraggingItems(ioNode, pointer, true)\n pointer.onDragEnd ??= (eUp) => canvas.#processDraggedItems(eUp)\n return true\n }\n }\n\n // Reroutes\n if (this.links_render_mode !== LinkRenderType.HIDDEN_LINK) {\n // Try layout store first for hit detection\n const rerouteLayout = layoutStore.queryRerouteAtPoint({ x, y })\n let foundReroute: Reroute | undefined\n\n if (rerouteLayout) {\n foundReroute = graph.getReroute(rerouteLayout.id)\n }\n\n // Fallback to checking visible reroutes directly\n for (const reroute of this.#visibleReroutes) {\n const overReroute =\n foundReroute === reroute || reroute.containsPoint([x, y])\n if (!reroute.isSlotHovered && !overReroute) continue\n\n if (overReroute) {\n pointer.onClick = () => this.processSelect(reroute, e)\n if (!e.shiftKey) {\n pointer.onDragStart = (pointer) =>\n this.#startDraggingItems(reroute, pointer, true)\n pointer.onDragEnd = (e) => this.#processDraggedItems(e)\n }\n }\n\n if (reroute.isOutputHovered || (overReroute && e.shiftKey)) {\n linkConnector.dragFromReroute(graph, reroute)\n this.#linkConnectorDrop()\n }\n\n if (reroute.isInputHovered) {\n linkConnector.dragFromRerouteToOutput(graph, reroute)\n this.#linkConnectorDrop()\n }\n\n reroute.hideSlots()\n this.dirty_bgcanvas = true\n return\n }\n }\n\n // Links - paths of links & reroutes\n // Set the width of the line for isPointInStroke checks\n const { lineWidth } = this.ctx\n this.ctx.lineWidth = this.connections_width + 7\n const dpi = Math.max(window?.devicePixelRatio ?? 1, 1)\n\n // Try layout store for segment hit testing first (more precise)\n const hitSegment = layoutStore.queryLinkSegmentAtPoint({ x, y }, this.ctx)\n\n for (const linkSegment of this.renderedPaths) {\n const centre = linkSegment._pos\n if (!centre) continue\n\n // Check if this link segment was hit\n let isLinkHit =\n hitSegment &&\n linkSegment.id ===\n (linkSegment instanceof Reroute\n ? hitSegment.rerouteId\n : hitSegment.linkId)\n\n if (!isLinkHit && linkSegment.path) {\n // Fallback to direct path hit testing if not found in layout store\n isLinkHit = this.ctx.isPointInStroke(\n linkSegment.path,\n x * dpi,\n y * dpi\n )\n }\n\n // If we shift click on a link then start a link from that input\n if ((e.shiftKey || e.altKey) && isLinkHit) {\n this.ctx.lineWidth = lineWidth\n\n if (e.shiftKey && !e.altKey) {\n linkConnector.dragFromLinkSegment(graph, linkSegment)\n this.#linkConnectorDrop()\n\n return\n } else if (e.altKey && !e.shiftKey) {\n const newReroute = graph.createReroute([x, y], linkSegment)\n pointer.onDragStart = (pointer) =>\n this.#startDraggingItems(newReroute, pointer)\n pointer.onDragEnd = (e) => this.#processDraggedItems(e)\n return\n }\n } else if (\n this.linkMarkerShape !== LinkMarkerShape.None &&\n isInRectangle(x, y, centre[0] - 4, centre[1] - 4, 8, 8)\n ) {\n this.ctx.lineWidth = lineWidth\n\n pointer.onClick = () => this.showLinkMenu(linkSegment, e)\n pointer.onDragStart = () => (this.dragging_canvas = true)\n pointer.finally = () => (this.dragging_canvas = false)\n\n // clear tooltip\n this.over_link_center = undefined\n return\n }\n }\n\n // Restore line width\n this.ctx.lineWidth = lineWidth\n\n // Groups\n const group = graph.getGroupOnPos(x, y)\n this.selected_group = group ?? null\n if (group) {\n if (group.isInResize(x, y)) {\n // Resize group\n const b = group.boundingRect\n const offsetX = x - (b[0] + b[2])\n const offsetY = y - (b[1] + b[3])\n\n pointer.onDragStart = () => (this.resizingGroup = group)\n pointer.onDrag = (eMove) => {\n if (this.read_only) return\n\n // Resize only by the exact pointer movement\n const pos: Point = [\n eMove.canvasX - group.pos[0] - offsetX,\n eMove.canvasY - group.pos[1] - offsetY\n ]\n // Unless snapping.\n if (this.#snapToGrid) snapPoint(pos, this.#snapToGrid)\n\n const resized = group.resize(pos[0], pos[1])\n if (resized) this.dirty_bgcanvas = true\n }\n pointer.finally = () => (this.resizingGroup = null)\n } else {\n const f = group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE\n const headerHeight = f * 1.4\n if (\n isInRectangle(\n x,\n y,\n group.pos[0],\n group.pos[1],\n group.size[0],\n headerHeight\n )\n ) {\n // In title bar\n pointer.onClick = () => this.processSelect(group, e)\n pointer.onDragStart = (pointer) => {\n group.recomputeInsideNodes()\n this.#startDraggingItems(group, pointer, true)\n }\n pointer.onDragEnd = (e) => this.#processDraggedItems(e)\n }\n }\n\n pointer.onDoubleClick = () => {\n this.emitEvent({\n subType: 'group-double-click',\n originalEvent: e,\n group\n })\n }\n } else {\n pointer.onDoubleClick = () => {\n // Double click within group should not trigger the searchbox.\n if (this.allow_searchbox) {\n this.showSearchBox(e)\n e.preventDefault()\n }\n this.emitEvent({\n subType: 'empty-double-click',\n originalEvent: e\n })\n }\n }\n }\n\n if (\n !pointer.onDragStart &&\n !pointer.onClick &&\n !pointer.onDrag &&\n this.allow_dragcanvas\n ) {\n // allow dragging canvas based on leftMouseClickBehavior or read-only mode\n if (LiteGraph.leftMouseClickBehavior === 'panning' || this.read_only) {\n pointer.onClick = () => this.processSelect(null, e)\n pointer.finally = () => (this.dragging_canvas = false)\n this.dragging_canvas = true\n } else {\n this.#setupNodeSelectionDrag(e, pointer)\n }\n }\n }\n\n #setupNodeSelectionDrag(\n e: CanvasPointerEvent,\n pointer: CanvasPointer,\n node?: LGraphNode | undefined\n ): void {\n const dragRect: Rect = [0, 0, 0, 0]\n\n dragRect[0] = e.canvasX\n dragRect[1] = e.canvasY\n dragRect[2] = 1\n dragRect[3] = 1\n\n pointer.onClick = (eUp) => {\n // Click, not drag\n const clickedItem =\n node ?? this.#getPositionableOnPos(eUp.canvasX, eUp.canvasY)\n this.processSelect(clickedItem, eUp)\n }\n pointer.onDragStart = () => (this.dragging_rectangle = dragRect)\n\n if (this.liveSelection) {\n const initialSelection = new Set(this.selectedItems)\n\n pointer.onDrag = (eMove) =>\n this.handleLiveSelect(eMove, dragRect, initialSelection)\n\n pointer.onDragEnd = () => this.finalizeLiveSelect()\n } else {\n // Classic mode: select only when drag ends\n pointer.onDragEnd = (upEvent) =>\n this.#handleMultiSelect(upEvent, dragRect)\n }\n\n pointer.finally = () => (this.dragging_rectangle = null)\n }\n\n /**\n * Processes a pointerdown event inside the bounds of a node. Part of {@link processMouseDown}.\n * @param e The pointerdown event\n * @param ctrlOrMeta Ctrl or meta key is pressed\n * @param node The node to process a click event for\n */\n #processNodeClick(\n e: CanvasPointerEvent,\n ctrlOrMeta: boolean,\n node: LGraphNode\n ): void {\n // In Vue nodes mode, Vue components own all node-level interactions\n // Skip LiteGraph handling to prevent dual event processing\n if (LiteGraph.vueNodesMode) {\n return\n }\n\n const { pointer, graph, linkConnector } = this\n if (!graph) throw new NullGraphError()\n\n const x = e.canvasX\n const y = e.canvasY\n\n pointer.onClick = () => this.processSelect(node, e)\n\n // Immediately bring to front\n if (!node.flags.pinned) {\n this.bringToFront(node)\n }\n\n // Collapse toggle\n const inCollapse = node.isPointInCollapse(x, y)\n if (inCollapse) {\n pointer.onClick = () => {\n node.collapse()\n this.setDirty(true, true)\n }\n } else if (!node.flags.collapsed) {\n const { inputs, outputs } = node\n\n function hasRelevantOutputLinks(\n output: INodeOutputSlot,\n network: LinkNetwork\n ): boolean {\n const outputLinks = [\n ...(output.links ?? []),\n ...[...(output._floatingLinks ?? new Set())]\n ]\n return outputLinks.some(\n (linkId) =>\n typeof linkId === 'number' && network.getLink(linkId) !== undefined\n )\n }\n\n // Outputs\n if (outputs) {\n for (const [i, output] of outputs.entries()) {\n const link_pos = node.getOutputPos(i)\n if (isInRectangle(x, y, link_pos[0] - 15, link_pos[1] - 10, 30, 20)) {\n // Drag multiple output links\n if (e.shiftKey && hasRelevantOutputLinks(output, graph)) {\n linkConnector.moveOutputLink(graph, output)\n this.#linkConnectorDrop()\n return\n }\n\n // New output link\n linkConnector.dragNewFromOutput(graph, node, output)\n this.#linkConnectorDrop()\n\n if (LiteGraph.shift_click_do_break_link_from) {\n if (e.shiftKey) {\n node.disconnectOutput(i)\n }\n } else if (LiteGraph.ctrl_alt_click_do_break_link) {\n if (ctrlOrMeta && e.altKey && !e.shiftKey) {\n node.disconnectOutput(i)\n }\n }\n\n // TODO: Move callbacks to the start of this closure (onInputClick is already correct).\n pointer.onDoubleClick = () => node.onOutputDblClick?.(i, e)\n pointer.onClick = () => node.onOutputClick?.(i, e)\n\n return\n }\n }\n }\n\n // Inputs\n if (inputs) {\n for (const [i, input] of inputs.entries()) {\n const link_pos = node.getInputPos(i)\n const isInSlot =\n input instanceof NodeInputSlot\n ? isInRect(x, y, input.boundingRect)\n : isInRectangle(x, y, link_pos[0] - 15, link_pos[1] - 10, 30, 20)\n\n if (isInSlot) {\n pointer.onDoubleClick = () => node.onInputDblClick?.(i, e)\n pointer.onClick = () => node.onInputClick?.(i, e)\n\n const shouldBreakLink =\n LiteGraph.ctrl_alt_click_do_break_link &&\n ctrlOrMeta &&\n e.altKey &&\n !e.shiftKey\n if (input.link !== null || input._floatingLinks?.size) {\n // Existing link\n if (shouldBreakLink || LiteGraph.click_do_break_link_to) {\n node.disconnectInput(i, true)\n } else if (e.shiftKey || this.allow_reconnect_links) {\n linkConnector.moveInputLink(graph, input)\n }\n }\n\n // Dragging a new link from input to output\n if (!linkConnector.isConnecting) {\n linkConnector.dragNewFromInput(graph, node, input)\n }\n\n this.#linkConnectorDrop()\n this.dirty_bgcanvas = true\n\n return\n }\n }\n }\n }\n\n // Click was inside the node, but not on input/output, or resize area\n const pos: Point = [x - node.pos[0], y - node.pos[1]]\n\n // Widget\n const widget = node.getWidgetOnPos(x, y)\n if (widget) {\n this.processWidgetClick(e, node, widget)\n this.node_widget = [node, widget]\n } else {\n // Node background\n pointer.onDoubleClick = () => {\n // Double-click\n // Check if it's a double click on the title bar\n // Note: pos[1] is the y-coordinate of the node's body\n // If clicking on node header (title), pos[1] is negative\n if (pos[1] < 0 && !inCollapse) {\n node.onNodeTitleDblClick?.(e, pos, this)\n } else if (node instanceof SubgraphNode) {\n this.openSubgraph(node.subgraph, node)\n }\n\n node.onDblClick?.(e, pos, this)\n this.emitEvent({\n subType: 'node-double-click',\n originalEvent: e,\n node\n })\n this.processNodeDblClicked(node)\n }\n\n // Check for title button clicks before calling onMouseDown\n if (node.title_buttons?.length && !node.flags.collapsed) {\n // pos contains the offset from the node's position, so we need to use node-relative coordinates\n const nodeRelativeX = pos[0]\n const nodeRelativeY = pos[1]\n\n for (let i = 0; i < node.title_buttons.length; i++) {\n const button = node.title_buttons[i]\n if (\n button.visible &&\n button.isPointInside(nodeRelativeX, nodeRelativeY)\n ) {\n node.onTitleButtonClick(button, this)\n // Set a no-op click handler to prevent fallback canvas dragging\n pointer.onClick = () => {}\n return\n }\n }\n }\n\n // Mousedown callback - can block drag\n if (node.onMouseDown?.(e, pos, this)) {\n // Node handled the event (e.g., title button clicked)\n // Set a no-op click handler to prevent fallback canvas dragging\n pointer.onClick = () => {}\n return\n }\n\n if (!this.allow_dragnodes) return\n\n // Check for resize AFTER checking all other interaction areas\n if (!node.flags.collapsed) {\n const resizeDirection = node.findResizeDirection(x, y)\n if (resizeDirection) {\n pointer.resizeDirection = resizeDirection\n const startBounds = new Rectangle(\n node.pos[0],\n node.pos[1],\n node.size[0],\n node.size[1]\n )\n\n pointer.onDragStart = () => {\n graph.beforeChange()\n this.resizing_node = node\n }\n\n pointer.onDrag = (eMove) => {\n if (this.read_only) return\n\n const deltaX = eMove.canvasX - x\n const deltaY = eMove.canvasY - y\n\n const newBounds = new Rectangle(\n startBounds.x,\n startBounds.y,\n startBounds.width,\n startBounds.height\n )\n\n // Handle resize based on the direction\n switch (resizeDirection) {\n case 'NE': // North-East (top-right)\n newBounds.y = startBounds.y + deltaY\n newBounds.width = startBounds.width + deltaX\n newBounds.height = startBounds.height - deltaY\n break\n case 'SE': // South-East (bottom-right)\n newBounds.width = startBounds.width + deltaX\n newBounds.height = startBounds.height + deltaY\n break\n case 'SW': // South-West (bottom-left)\n newBounds.x = startBounds.x + deltaX\n newBounds.width = startBounds.width - deltaX\n newBounds.height = startBounds.height + deltaY\n break\n case 'NW': // North-West (top-left)\n newBounds.x = startBounds.x + deltaX\n newBounds.y = startBounds.y + deltaY\n newBounds.width = startBounds.width - deltaX\n newBounds.height = startBounds.height - deltaY\n break\n }\n\n // Apply snapping to position changes\n if (this.#snapToGrid) {\n if (\n resizeDirection.includes('N') ||\n resizeDirection.includes('W')\n ) {\n const originalX = newBounds.x\n const originalY = newBounds.y\n\n snapPoint(newBounds.pos, this.#snapToGrid)\n\n // Adjust size to compensate for snapped position\n if (resizeDirection.includes('N')) {\n newBounds.height += originalY - newBounds.y\n }\n if (resizeDirection.includes('W')) {\n newBounds.width += originalX - newBounds.x\n }\n }\n\n snapPoint(newBounds.size, this.#snapToGrid)\n }\n\n // Apply snapping to size changes\n\n // Enforce minimum size\n const min = node.computeSize()\n if (newBounds.width < min[0]) {\n // If resizing from left, adjust position to maintain right edge\n if (resizeDirection.includes('W')) {\n newBounds.x = startBounds.x + startBounds.width - min[0]\n }\n newBounds.width = min[0]\n }\n if (newBounds.height < min[1]) {\n // If resizing from top, adjust position to maintain bottom edge\n if (resizeDirection.includes('N')) {\n newBounds.y = startBounds.y + startBounds.height - min[1]\n }\n newBounds.height = min[1]\n }\n\n node.pos = newBounds.pos\n node.setSize(newBounds.size)\n\n this.#dirty()\n }\n\n pointer.onDragEnd = () => {\n this.#dirty()\n graph.afterChange(node)\n }\n pointer.finally = () => {\n this.resizing_node = null\n pointer.resizeDirection = undefined\n }\n\n // Set appropriate cursor for resize direction\n this.canvas.style.cursor = cursors[resizeDirection]\n return\n }\n }\n\n // Drag node\n pointer.onDragStart = (pointer) =>\n this.#startDraggingItems(node, pointer, true)\n pointer.onDragEnd = (e) => this.#processDraggedItems(e)\n }\n\n this.dirty_canvas = true\n }\n\n processWidgetClick(\n e: CanvasPointerEvent,\n node: LGraphNode,\n widget: IBaseWidget,\n pointer = this.pointer\n ) {\n // Custom widget - CanvasPointer\n if (typeof widget.onPointerDown === 'function') {\n const handled = widget.onPointerDown(pointer, node, this)\n if (handled) return\n }\n\n const oldValue = widget.value\n\n const pos = this.graph_mouse\n const x = pos[0] - node.pos[0]\n const y = pos[1] - node.pos[1]\n\n const widgetInstance = toConcreteWidget(widget, node, false)\n if (widgetInstance) {\n pointer.onClick = () =>\n widgetInstance.onClick({\n e,\n node,\n canvas: this\n })\n pointer.onDrag = (eMove) =>\n widgetInstance.onDrag?.({\n e: eMove,\n node,\n canvas: this\n })\n } else if (widget.mouse) {\n const result = widget.mouse(e, [x, y], node)\n if (result != null) this.dirty_canvas = result\n }\n\n // value changed\n if (oldValue != widget.value) {\n node.onWidgetChanged?.(widget.name, widget.value, oldValue, widget)\n if (!node.graph) throw new NullGraphError()\n node.graph._version++\n }\n\n // Clean up state var\n pointer.finally = () => {\n // Legacy custom widget callback\n if (widget.mouse) {\n const { eUp } = pointer\n if (!eUp) return\n const { canvasX, canvasY } = eUp\n widget.mouse(eUp, [canvasX - node.pos[0], canvasY - node.pos[1]], node)\n }\n\n this.node_widget = null\n }\n }\n\n /**\n * Pointer middle button click processing. Part of {@link processMouseDown}.\n * @param e The pointerdown event\n * @param node The node to process a click event for\n */\n #processMiddleButton(e: CanvasPointerEvent, node: LGraphNode | undefined) {\n const { pointer } = this\n\n if (\n LiteGraph.middle_click_slot_add_default_node &&\n node &&\n this.allow_interaction &&\n !this.read_only &&\n !this.connecting_links &&\n !node.flags.collapsed\n ) {\n // not dragging mouse to connect two slots\n let mClikSlot: INodeSlot | false = false\n let mClikSlot_index: number | false = false\n let mClikSlot_isOut: boolean = false\n const { inputs, outputs } = node\n\n // search for outputs\n if (outputs) {\n for (const [i, output] of outputs.entries()) {\n const link_pos = node.getOutputPos(i)\n if (\n isInRectangle(\n e.canvasX,\n e.canvasY,\n link_pos[0] - 15,\n link_pos[1] - 10,\n 30,\n 20\n )\n ) {\n mClikSlot = output\n mClikSlot_index = i\n mClikSlot_isOut = true\n break\n }\n }\n }\n\n // search for inputs\n if (inputs) {\n for (const [i, input] of inputs.entries()) {\n const link_pos = node.getInputPos(i)\n if (\n isInRectangle(\n e.canvasX,\n e.canvasY,\n link_pos[0] - 15,\n link_pos[1] - 10,\n 30,\n 20\n )\n ) {\n mClikSlot = input\n mClikSlot_index = i\n mClikSlot_isOut = false\n break\n }\n }\n }\n // Middle clicked a slot\n if (mClikSlot && mClikSlot_index !== false) {\n const alphaPosY =\n 0.5 -\n (mClikSlot_index + 1) /\n (mClikSlot_isOut ? outputs.length : inputs.length)\n const node_bounding = node.getBounding()\n // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with\n // a correct autoplacement that knows about the others slots and nodes\n const posRef: Point = [\n !mClikSlot_isOut\n ? node_bounding[0]\n : node_bounding[0] + node_bounding[2],\n e.canvasY - 80\n ]\n\n pointer.onClick = () =>\n this.createDefaultNodeForSlot({\n nodeFrom: !mClikSlot_isOut ? null : node,\n slotFrom: !mClikSlot_isOut ? null : mClikSlot_index,\n nodeTo: !mClikSlot_isOut ? node : null,\n slotTo: !mClikSlot_isOut ? mClikSlot_index : null,\n position: posRef,\n nodeType: 'AUTO',\n posAdd: [!mClikSlot_isOut ? -30 : 30, -alphaPosY * 130],\n posSizeFix: [!mClikSlot_isOut ? -1 : 0, 0]\n })\n }\n }\n\n // Drag canvas using middle mouse button\n if (this.allow_dragcanvas) {\n pointer.onDragStart = () => (this.dragging_canvas = true)\n pointer.finally = () => (this.dragging_canvas = false)\n }\n }\n\n #processDragZoom(e: PointerEvent): void {\n // stop canvas zoom action\n if (!e.buttons) {\n this.#finishDragZoom()\n return\n }\n\n const start = this.#dragZoomStart\n if (!start) throw new TypeError('Drag-zoom state object was null')\n if (!this.graph) throw new NullGraphError()\n\n // calculate delta\n const deltaY = e.y - start.pos[1]\n const startScale = start.scale\n\n const scale = startScale - deltaY / 100\n\n this.ds.changeScale(scale, start.pos)\n this.graph.change()\n }\n\n #finishDragZoom(): void {\n const start = this.#dragZoomStart\n if (!start) return\n this.#dragZoomStart = null\n this.read_only = start.readOnly\n }\n\n /**\n * Called when a mouse move event has to be processed\n */\n processMouseMove(e: PointerEvent): void {\n if (\n this.dragZoomEnabled &&\n e.ctrlKey &&\n e.shiftKey &&\n this.#dragZoomStart\n ) {\n this.#processDragZoom(e)\n return\n }\n\n if (this.autoresize) this.resize()\n\n if (this.set_canvas_dirty_on_mouse_event) this.dirty_canvas = true\n\n const { graph, resizingGroup, linkConnector, pointer, subgraph } = this\n if (!graph) return\n\n LGraphCanvas.active_canvas = this\n this.adjustMouseEvent(e)\n const mouse: Readonly<Point> = [e.clientX, e.clientY]\n this.mouse[0] = mouse[0]\n this.mouse[1] = mouse[1]\n const delta = [mouse[0] - this.last_mouse[0], mouse[1] - this.last_mouse[1]]\n this.last_mouse = mouse\n const { canvasX: x, canvasY: y } = e\n this.graph_mouse[0] = x\n this.graph_mouse[1] = y\n\n if (e.isPrimary) pointer.move(e)\n\n /** See {@link state}.{@link LGraphCanvasState.hoveringOver hoveringOver} */\n let underPointer = CanvasItem.Nothing\n if (subgraph) {\n underPointer |= subgraph.inputNode.onPointerMove(e)\n underPointer |= subgraph.outputNode.onPointerMove(e)\n }\n\n if (this.block_click) {\n e.preventDefault()\n return\n }\n\n e.dragging = this.last_mouse_dragging\n\n if (this.node_widget) {\n // Legacy widget mouse callbacks for pointermove events\n const [node, widget] = this.node_widget\n\n if (widget?.mouse) {\n const relativeX = x - node.pos[0]\n const relativeY = y - node.pos[1]\n const result = widget.mouse(e, [relativeX, relativeY], node)\n if (result != null) this.dirty_canvas = result\n }\n }\n\n // get node over\n const node = LiteGraph.vueNodesMode\n ? null\n : graph.getNodeOnPos(x, y, this.visible_nodes)\n\n const dragRect = this.dragging_rectangle\n if (dragRect) {\n dragRect[2] = x - dragRect[0]\n dragRect[3] = y - dragRect[1]\n this.dirty_canvas = true\n } else if (resizingGroup) {\n // Resizing a group\n underPointer |= CanvasItem.Group\n pointer.resizeDirection = 'SE'\n } else if (this.dragging_canvas) {\n this.ds.offset[0] += delta[0] / this.ds.scale\n this.ds.offset[1] += delta[1] / this.ds.scale\n this.#dirty()\n } else if (\n (this.allow_interaction || node?.flags.allow_interaction) &&\n !this.read_only\n ) {\n if (linkConnector.isConnecting) this.dirty_canvas = true\n\n // remove mouseover flag\n this.updateMouseOverNodes(node, e)\n\n // mouse over a node\n if (node) {\n underPointer |= CanvasItem.Node\n\n if (node.redraw_on_mouse) this.dirty_canvas = true\n\n // For input/output hovering\n // to store the output of isOverNodeInput\n const pos: Point = [0, 0]\n\n // Try to use layout store for hit testing first, fallback to old method\n let inputId: number = -1\n let outputId: number = -1\n\n const slotLayout = layoutStore.querySlotAtPoint({ x, y })\n if (slotLayout && slotLayout.nodeId === String(node.id)) {\n if (slotLayout.type === 'input') {\n inputId = slotLayout.index\n pos[0] = slotLayout.position.x\n pos[1] = slotLayout.position.y\n } else {\n outputId = slotLayout.index\n pos[0] = slotLayout.position.x\n pos[1] = slotLayout.position.y\n }\n } else {\n // Fallback to old method\n inputId = isOverNodeInput(node, x, y, pos)\n outputId = isOverNodeOutput(node, x, y, pos)\n }\n const overWidget = node.getWidgetOnPos(x, y, true) ?? undefined\n\n if (!node.mouseOver) {\n // mouse enter\n node.mouseOver = {}\n this.node_over = node\n this.dirty_canvas = true\n\n for (const reroute of this.#visibleReroutes) {\n reroute.hideSlots()\n this.dirty_bgcanvas = true\n }\n node.onMouseEnter?.(e)\n }\n\n // in case the node wants to do something\n node.onMouseMove?.(e, [x - node.pos[0], y - node.pos[1]], this)\n\n // The input the mouse is over has changed\n const { mouseOver } = node\n if (\n mouseOver.inputId !== inputId ||\n mouseOver.outputId !== outputId ||\n mouseOver.overWidget !== overWidget\n ) {\n mouseOver.inputId = inputId\n mouseOver.outputId = outputId\n mouseOver.overWidget = overWidget\n\n // State reset\n linkConnector.overWidget = undefined\n\n // Check if link is over anything it could connect to - record position of valid target for snap / highlight\n if (linkConnector.isConnecting) {\n const firstLink = linkConnector.renderLinks.at(0)\n\n // Default: nothing highlighted\n let highlightPos: Point | undefined\n let highlightInput: INodeInputSlot | undefined\n\n if (!firstLink || !linkConnector.isNodeValidDrop(node)) {\n // No link, or none of the dragged links may be dropped here\n } else if (linkConnector.state.connectingTo === 'input') {\n if (overWidget) {\n // Check widgets first - inputId is only valid if over the input socket\n const slot = node.getSlotFromWidget(overWidget)\n\n if (slot && linkConnector.isInputValidDrop(node, slot)) {\n highlightInput = slot\n if (LiteGraph.vueNodesMode) {\n const idx = node.inputs.indexOf(slot)\n highlightPos =\n idx !== -1\n ? getSlotPosition(node, idx, true)\n : node.getInputSlotPos(slot)\n } else {\n highlightPos = node.getInputSlotPos(slot)\n }\n linkConnector.overWidget = overWidget\n }\n }\n\n // Not over a valid widget - treat drop on invalid widget same as node background\n if (!linkConnector.overWidget) {\n if (inputId === -1 && outputId === -1) {\n // Node background / title under the pointer\n const result = node.findInputByType(firstLink.fromSlot.type)\n if (result) {\n highlightInput = result.slot\n highlightPos = LiteGraph.vueNodesMode\n ? getSlotPosition(node, result.index, true)\n : node.getInputSlotPos(result.slot)\n }\n } else if (\n inputId != -1 &&\n node.inputs[inputId] &&\n LiteGraph.isValidConnection(\n firstLink.fromSlot.type,\n node.inputs[inputId].type\n )\n ) {\n highlightPos = pos\n // XXX CHECK THIS\n highlightInput = node.inputs[inputId]\n }\n\n if (highlightInput) {\n const widget = node.getWidgetFromSlot(highlightInput)\n if (widget) linkConnector.overWidget = widget\n }\n }\n } else if (linkConnector.state.connectingTo === 'output') {\n // Connecting from an input to an output\n if (inputId === -1 && outputId === -1) {\n const result = node.findOutputByType(firstLink.fromSlot.type)\n if (result) {\n highlightPos = LiteGraph.vueNodesMode\n ? getSlotPosition(node, result.index, false)\n : node.getOutputPos(result.index)\n }\n } else {\n // check if I have a slot below de mouse\n if (\n outputId != -1 &&\n node.outputs[outputId] &&\n LiteGraph.isValidConnection(\n firstLink.fromSlot.type,\n node.outputs[outputId].type\n )\n ) {\n highlightPos = pos\n }\n }\n }\n this._highlight_pos = highlightPos\n this._highlight_input = highlightInput\n }\n\n this.dirty_canvas = true\n }\n\n // Resize direction - only show resize cursor if not over inputs/outputs/widgets\n if (!pointer.eDown) {\n if (inputId === -1 && outputId === -1 && !overWidget) {\n pointer.resizeDirection = node.findResizeDirection(x, y)\n } else {\n // Clear resize direction when over inputs/outputs/widgets\n pointer.resizeDirection &&= undefined\n }\n }\n } else {\n // Reroutes\n underPointer = this.#updateReroutes(underPointer)\n\n // Not over a node\n const segment = this.#getLinkCentreOnPos(e)\n if (this.over_link_center !== segment) {\n underPointer |= CanvasItem.Link\n this.over_link_center = segment\n this.dirty_bgcanvas = true\n }\n\n if (this.canvas) {\n const group = graph.getGroupOnPos(x, y)\n if (\n group &&\n !e.ctrlKey &&\n !this.read_only &&\n group.isInResize(x, y)\n ) {\n pointer.resizeDirection = 'SE'\n } else {\n pointer.resizeDirection &&= undefined\n }\n }\n }\n\n // send event to node if capturing input (used with widgets that allow drag outside of the area of the node)\n if (this.node_capturing_input && this.node_capturing_input != node) {\n this.node_capturing_input.onMouseMove?.(\n e,\n [\n x - this.node_capturing_input.pos[0],\n y - this.node_capturing_input.pos[1]\n ],\n this\n )\n }\n\n // Items being dragged\n if (this.isDragging) {\n const selected = this.selectedItems\n const allItems = e.ctrlKey ? selected : getAllNestedItems(selected)\n\n const deltaX = delta[0] / this.ds.scale\n const deltaY = delta[1] / this.ds.scale\n\n if (LiteGraph.vueNodesMode) {\n this.moveChildNodesInGroupVueMode(allItems, deltaX, deltaY)\n } else {\n for (const item of allItems) {\n item.move(deltaX, deltaY, true)\n }\n }\n\n this.#dirty()\n }\n }\n\n this.hoveringOver = underPointer\n\n e.preventDefault()\n return\n }\n\n /**\n * Updates the hover / snap state of all visible reroutes.\n * @returns The original value of {@link underPointer}, with any found reroute items added.\n */\n #updateReroutes(underPointer: CanvasItem): CanvasItem {\n const { graph, pointer, linkConnector } = this\n if (!graph) throw new NullGraphError()\n\n // Update reroute hover state\n if (!pointer.isDown) {\n let anyChanges = false\n for (const reroute of this.#visibleReroutes) {\n anyChanges ||= reroute.updateVisibility(this.graph_mouse)\n\n if (reroute.isSlotHovered) underPointer |= CanvasItem.RerouteSlot\n }\n if (anyChanges) this.dirty_bgcanvas = true\n } else if (linkConnector.isConnecting) {\n // Highlight the reroute that the mouse is over\n for (const reroute of this.#visibleReroutes) {\n if (reroute.containsPoint(this.graph_mouse)) {\n if (linkConnector.isRerouteValidDrop(reroute)) {\n linkConnector.overReroute = reroute\n this._highlight_pos = reroute.pos\n }\n\n return (underPointer |= CanvasItem.RerouteSlot)\n }\n }\n }\n\n this._highlight_pos &&= undefined\n linkConnector.overReroute &&= undefined\n return underPointer\n }\n\n /**\n * Start dragging an item, optionally including all other selected items.\n *\n * ** This function sets the {@link CanvasPointer.finally}() callback. **\n * @param item The item that the drag event started on\n * @param pointer The pointer event that initiated the drag, e.g. pointerdown\n * @param sticky If `true`, the item is added to the selection - see {@link processSelect}\n */\n #startDraggingItems(\n item: Positionable,\n pointer: CanvasPointer,\n sticky = false\n ): void {\n this.emitBeforeChange()\n this.graph?.beforeChange()\n // Ensure that dragging is properly cleaned up, on success or failure.\n pointer.finally = () => {\n this.isDragging = false\n this.graph?.afterChange()\n this.emitAfterChange()\n }\n\n this.processSelect(item, pointer.eDown, sticky)\n this.isDragging = true\n }\n\n /**\n * Handles shared clean up and placement after items have been dragged.\n * @param e The event that completed the drag, e.g. pointerup, pointermove\n */\n #processDraggedItems(e: CanvasPointerEvent): void {\n const { graph } = this\n if (e.shiftKey || LiteGraph.alwaysSnapToGrid)\n graph?.snapToGrid(this.selectedItems)\n\n this.dirty_canvas = true\n this.dirty_bgcanvas = true\n\n // TODO: Replace legacy behaviour: callbacks were never extended for multiple items\n this.onNodeMoved?.(findFirstNode(this.selectedItems))\n }\n\n /**\n * Called when a mouse up event has to be processed\n */\n processMouseUp(e: PointerEvent): void {\n // early exit for extra pointer\n if (e.isPrimary === false) return\n\n const { graph, pointer } = this\n if (!graph) return\n\n this.#finishDragZoom()\n\n LGraphCanvas.active_canvas = this\n\n this.adjustMouseEvent(e)\n\n const now = LiteGraph.getTime()\n e.click_time = now - this.last_mouseclick\n\n /** The mouseup event occurred near the mousedown event. */\n /** Normal-looking click event - mouseUp occurred near mouseDown, without dragging. */\n const isClick = pointer.up(e)\n if (isClick === true) {\n pointer.isDown = false\n pointer.isDouble = false\n // Required until all link behaviour is added to Pointer API\n this.connecting_links = null\n this.dragging_canvas = false\n\n graph.change()\n\n e.stopPropagation()\n e.preventDefault()\n return\n }\n\n this.last_mouse_dragging = false\n this.last_click_position = null\n\n // used to avoid sending twice a click in an immediate button\n this.block_click &&= false\n\n if (e.button === 0) {\n // left button\n this.selected_group = null\n\n this.isDragging = false\n\n const x = e.canvasX\n const y = e.canvasY\n\n if (!this.linkConnector.isConnecting) {\n this.dirty_canvas = true\n\n this.node_over?.onMouseUp?.(\n e,\n [x - this.node_over.pos[0], y - this.node_over.pos[1]],\n this\n )\n this.node_capturing_input?.onMouseUp?.(\n e,\n [\n x - this.node_capturing_input.pos[0],\n y - this.node_capturing_input.pos[1]\n ],\n this\n )\n }\n } else if (e.button === 1) {\n // middle button\n this.dirty_canvas = true\n this.dragging_canvas = false\n } else if (e.button === 2) {\n // right button\n this.dirty_canvas = true\n }\n\n pointer.isDown = false\n pointer.isDouble = false\n\n graph.change()\n\n e.stopPropagation()\n e.preventDefault()\n return\n }\n\n /**\n * Called when the mouse moves off the canvas. Clears all node hover states.\n * @param e\n */\n processMouseOut(e: PointerEvent): void {\n // TODO: Check if document.contains(e.relatedTarget) - handle mouseover node textarea etc.\n this.adjustMouseEvent(e)\n this.updateMouseOverNodes(null, e)\n }\n\n processMouseCancel(): void {\n console.warn('Pointer cancel!')\n this.pointer.reset()\n }\n\n /**\n * Called when a mouse wheel event has to be processed\n */\n processMouseWheel(e: WheelEvent): void {\n if (!this.graph || !this.allow_dragcanvas) return\n\n this.adjustMouseEvent(e)\n\n const pos: Point = [e.clientX, e.clientY]\n if (this.viewport && !isPointInRect(pos, this.viewport)) return\n\n let { scale } = this.ds\n\n // Detect if this is a trackpad gesture or mouse wheel\n const isTrackpad = this.pointer.isTrackpadGesture(e)\n const isCtrlOrMacMeta =\n e.ctrlKey || (e.metaKey && navigator.platform.includes('Mac'))\n const isZoomModifier = isCtrlOrMacMeta && !e.altKey && !e.shiftKey\n\n if (isZoomModifier || LiteGraph.mouseWheelScroll === 'zoom') {\n // Zoom mode or modifier key pressed - use wheel for zoom\n if (isTrackpad) {\n // Trackpad gesture - use smooth scaling\n scale *= 1 + e.deltaY * (1 - this.zoom_speed) * 0.18\n this.ds.changeScale(scale, [e.clientX, e.clientY], false)\n } else {\n // Mouse wheel - use stepped scaling\n if (e.deltaY < 0) {\n scale *= this.zoom_speed\n } else if (e.deltaY > 0) {\n scale *= 1 / this.zoom_speed\n }\n this.ds.changeScale(scale, [e.clientX, e.clientY])\n }\n } else {\n // Trackpads and mice work on significantly different scales\n const factor = isTrackpad ? 0.18 : 0.008_333\n\n if (!isTrackpad && e.shiftKey && e.deltaX === 0) {\n this.ds.offset[0] -= e.deltaY * (1 + factor) * (1 / scale)\n } else {\n this.ds.offset[0] -= e.deltaX * (1 + factor) * (1 / scale)\n this.ds.offset[1] -= e.deltaY * (1 + factor) * (1 / scale)\n }\n }\n\n this.graph.change()\n\n e.preventDefault()\n return\n }\n\n #noItemsSelected(): void {\n const event = new CustomEvent('litegraph:no-items-selected', {\n bubbles: true\n })\n this.canvas.dispatchEvent(event)\n }\n\n /**\n * process a key event\n */\n processKey(e: KeyboardEvent): void {\n this.#shiftDown = e.shiftKey\n\n const { graph } = this\n if (!graph) return\n\n let block_default = false\n // @ts-expect-error EventTarget.localName is not in standard types\n if (e.target.localName == 'input') return\n\n if (e.type == 'keydown') {\n // TODO: Switch\n if (e.key === ' ') {\n // space\n this.read_only = true\n if (this._previously_dragging_canvas === null) {\n this._previously_dragging_canvas = this.dragging_canvas\n }\n this.dragging_canvas = this.pointer.isDown\n block_default = true\n } else if (e.key === 'Escape') {\n // esc\n if (this.linkConnector.isConnecting) {\n this.linkConnector.reset()\n e.preventDefault()\n return\n }\n this.node_panel?.close()\n this.options_panel?.close()\n if (this.node_panel || this.options_panel) block_default = true\n } else if (e.keyCode === 65 && e.ctrlKey) {\n // select all Control A\n this.selectItems()\n block_default = true\n } else if (e.keyCode === 67 && (e.metaKey || e.ctrlKey) && !e.shiftKey) {\n // copy\n if (this.selected_nodes) {\n this.copyToClipboard()\n block_default = true\n }\n } else if (e.keyCode === 86 && (e.metaKey || e.ctrlKey)) {\n // paste\n this.pasteFromClipboard({ connectInputs: e.shiftKey })\n } else if (e.key === 'Delete' || e.key === 'Backspace') {\n // delete or backspace\n // @ts-expect-error EventTarget.localName is not in standard types\n if (e.target.localName != 'input' && e.target.localName != 'textarea') {\n if (this.selectedItems.size === 0) {\n this.#noItemsSelected()\n return\n }\n\n this.deleteSelected()\n block_default = true\n }\n }\n\n // TODO\n for (const node of Object.values(this.selected_nodes)) {\n node.onKeyDown?.(e)\n }\n } else if (e.type == 'keyup') {\n if (e.key === ' ') {\n // space\n this.read_only = false\n this.dragging_canvas =\n (this._previously_dragging_canvas ?? false) && this.pointer.isDown\n this._previously_dragging_canvas = null\n }\n\n for (const node of Object.values(this.selected_nodes)) {\n node.onKeyUp?.(e)\n }\n }\n\n // TODO: Do we need to remeasure and recalculate everything on every key down/up?\n graph.change()\n\n if (block_default) {\n e.preventDefault()\n e.stopImmediatePropagation()\n }\n }\n _serializeItems(items?: Iterable<Positionable>): ClipboardItems {\n const serialisable: Required<ClipboardItems> = {\n nodes: [],\n groups: [],\n reroutes: [],\n links: [],\n subgraphs: []\n }\n\n // NOTE: logic for traversing nested subgraphs depends on this being a set.\n const subgraphs = new Set<Subgraph>()\n\n // Create serialisable objects\n for (const item of items ?? this.selectedItems) {\n if (item instanceof LGraphNode) {\n // Nodes\n if (item.clonable === false) continue\n\n const cloned = item.clone()?.serialize()\n if (!cloned) continue\n\n cloned.id = item.id\n serialisable.nodes.push(cloned)\n\n // Links\n if (item.inputs) {\n for (const { link: linkId } of item.inputs) {\n if (linkId == null) continue\n\n const link = this.graph?._links.get(linkId)?.asSerialisable()\n if (link) serialisable.links.push(link)\n }\n }\n\n // Find all unique referenced subgraphs\n if (item instanceof SubgraphNode) {\n subgraphs.add(item.subgraph)\n }\n } else if (item instanceof LGraphGroup) {\n // Groups\n serialisable.groups.push(item.serialize())\n } else if (item instanceof Reroute) {\n // Reroutes\n serialisable.reroutes.push(item.asSerialisable())\n }\n }\n\n // Add unique subgraph entries\n // NOTE: subgraphs is appended to mid iteration.\n for (const subgraph of subgraphs) {\n for (const node of subgraph.nodes) {\n if (node instanceof SubgraphNode) {\n subgraphs.add(node.subgraph)\n }\n }\n const cloned = subgraph.clone(true).asSerialisable()\n serialisable.subgraphs.push(cloned)\n }\n return serialisable\n }\n\n /**\n * Copies canvas items to an internal, app-specific clipboard backed by local storage.\n * When called without parameters, it copies {@link selectedItems}.\n * @param items The items to copy. If nullish, all selected items are copied.\n */\n copyToClipboard(items?: Iterable<Positionable>): string {\n const serializedData = JSON.stringify(this._serializeItems(items))\n localStorage.setItem('litegrapheditor_clipboard', serializedData)\n return serializedData\n }\n\n emitEvent(detail: LGraphCanvasEventMap['litegraph:canvas']): void {\n this.canvas.dispatchEvent(\n new CustomEvent('litegraph:canvas', {\n bubbles: true,\n detail\n })\n )\n }\n\n /** @todo Refactor to where it belongs - e.g. Deleting / creating nodes is not actually canvas event. */\n emitBeforeChange(): void {\n this.emitEvent({\n subType: 'before-change'\n })\n }\n\n /** @todo See {@link emitBeforeChange} */\n emitAfterChange(): void {\n this.emitEvent({\n subType: 'after-change'\n })\n }\n\n /**\n * Pastes the items from the canvas \"clipbaord\" - a local storage variable.\n */\n _pasteFromClipboard(\n options: IPasteFromClipboardOptions = {}\n ): ClipboardPasteResult | undefined {\n const data = localStorage.getItem('litegrapheditor_clipboard')\n if (!data) return\n return this._deserializeItems(JSON.parse(data), options)\n }\n\n _deserializeItems(\n parsed: ClipboardItems,\n options: IPasteFromClipboardOptions\n ): ClipboardPasteResult | undefined {\n const { connectInputs = false, position = this.graph_mouse } = options\n\n // if ctrl + shift + v is off, return when isConnectUnselected is true (shift is pressed) to maintain old behavior\n if (\n !LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs &&\n connectInputs\n )\n return\n\n const { graph } = this\n if (!graph) throw new NullGraphError()\n graph.beforeChange()\n this.emitBeforeChange()\n\n // Parse & initialise\n parsed.nodes ??= []\n parsed.groups ??= []\n parsed.reroutes ??= []\n parsed.links ??= []\n parsed.subgraphs ??= []\n\n // Find top-left-most boundary\n let offsetX = Infinity\n let offsetY = Infinity\n for (const item of [...parsed.nodes, ...parsed.reroutes]) {\n if (item.pos == null)\n throw new TypeError(\n 'Invalid node encountered on paste. `pos` was null.'\n )\n\n if (item.pos[0] < offsetX) offsetX = item.pos[0]\n if (item.pos[1] < offsetY) offsetY = item.pos[1]\n }\n\n // TODO: Remove when implementing `asSerialisable`\n if (parsed.groups) {\n for (const group of parsed.groups) {\n if (group.bounding[0] < offsetX) offsetX = group.bounding[0]\n if (group.bounding[1] < offsetY) offsetY = group.bounding[1]\n }\n }\n\n const results: ClipboardPasteResult = {\n created: [],\n nodes: new Map<NodeId, LGraphNode>(),\n links: new Map<LinkId, LLink>(),\n reroutes: new Map<RerouteId, Reroute>(),\n subgraphs: new Map<UUID, Subgraph>()\n }\n const { created, nodes, links, reroutes } = results\n\n // const failedNodes: ISerialisedNode[] = []\n const subgraphIdMap: Record<string, string> = {}\n // SubgraphV2: Remove always-clone behaviour\n //Update subgraph ids\n for (const subgraphInfo of parsed.subgraphs)\n subgraphInfo.id = subgraphIdMap[subgraphInfo.id] = createUuidv4()\n const allNodeInfo: ISerialisedNode[] = [\n parsed.nodes ? [parsed.nodes] : [],\n parsed.subgraphs ? parsed.subgraphs.map((s) => s.nodes ?? []) : []\n ].flat(2)\n for (const nodeInfo of allNodeInfo)\n if (nodeInfo.type in subgraphIdMap)\n nodeInfo.type = subgraphIdMap[nodeInfo.type]\n\n // Subgraphs\n for (const info of parsed.subgraphs) {\n const subgraph = graph.createSubgraph(info)\n results.subgraphs.set(info.id, subgraph)\n }\n for (const info of parsed.subgraphs)\n results.subgraphs.get(info.id)?.configure(info)\n\n // Groups\n for (const info of parsed.groups) {\n info.id = -1\n\n const group = new LGraphGroup()\n group.configure(info)\n graph.add(group)\n created.push(group)\n }\n\n // Nodes\n for (const info of parsed.nodes) {\n const node = info.type == null ? null : LiteGraph.createNode(info.type)\n if (!node) {\n // failedNodes.push(info)\n continue\n }\n\n nodes.set(info.id, node)\n info.id = -1\n\n node.configure(info)\n graph.add(node)\n\n created.push(node)\n }\n\n // Reroutes\n for (const info of parsed.reroutes) {\n const { id, ...rerouteInfo } = info\n\n const reroute = graph.setReroute(rerouteInfo)\n created.push(reroute)\n reroutes.set(id, reroute)\n }\n\n // Remap reroute parentIds for pasted reroutes\n for (const reroute of reroutes.values()) {\n if (reroute.parentId == null) continue\n\n const mapped = reroutes.get(reroute.parentId)\n if (mapped) reroute.parentId = mapped.id\n }\n\n // Links\n for (const info of parsed.links) {\n // Find the copied node / reroute ID\n let outNode: LGraphNode | null | undefined = nodes.get(info.origin_id)\n let afterRerouteId: number | undefined\n if (info.parentId != null)\n afterRerouteId = reroutes.get(info.parentId)?.id\n\n // If it wasn't copied, use the original graph value\n if (\n connectInputs &&\n LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs\n ) {\n outNode ??= graph.getNodeById(info.origin_id)\n afterRerouteId ??= info.parentId\n }\n\n const inNode = nodes.get(info.target_id)\n if (inNode) {\n const link = outNode?.connect(\n info.origin_slot,\n inNode,\n info.target_slot,\n afterRerouteId\n )\n if (link) links.set(info.id, link)\n }\n }\n\n // Remap linkIds\n for (const reroute of reroutes.values()) {\n const ids = [...reroute.linkIds].map((x) => links.get(x)?.id ?? x)\n reroute.update(reroute.parentId, undefined, ids, reroute.floating)\n\n // Remove any invalid items\n if (!reroute.validateLinks(graph.links, graph.floatingLinks)) {\n graph.removeReroute(reroute.id)\n }\n }\n\n // Adjust positions\n for (const item of created) {\n item.pos[0] += position[0] - offsetX\n item.pos[1] += position[1] - offsetY\n }\n\n // TODO: Report failures, i.e. `failedNodes`\n\n const newPositions = created\n .filter((item): item is LGraphNode => item instanceof LGraphNode)\n .map((node) => {\n const fullHeight = node.size?.[1] ?? 200\n const layoutHeight = LiteGraph.vueNodesMode\n ? removeNodeTitleHeight(fullHeight)\n : fullHeight\n return {\n nodeId: String(node.id),\n bounds: {\n x: node.pos[0],\n y: node.pos[1],\n width: node.size?.[0] ?? 100,\n height: layoutHeight\n }\n }\n })\n\n if (newPositions.length) layoutStore.setSource(LayoutSource.Canvas)\n layoutStore.batchUpdateNodeBounds(newPositions)\n\n this.selectItems(created)\n forEachNode(graph, (n) => n.onGraphConfigured?.())\n forEachNode(graph, (n) => n.onAfterGraphConfigured?.())\n\n graph.afterChange()\n this.emitAfterChange()\n\n return results\n }\n\n pasteFromClipboard(options: IPasteFromClipboardOptions = {}): void {\n this.emitBeforeChange()\n try {\n this._pasteFromClipboard(options)\n } finally {\n this.emitAfterChange()\n }\n }\n\n processNodeDblClicked(n: LGraphNode): void {\n this.onShowNodePanel?.(n)\n this.onNodeDblClicked?.(n)\n\n this.setDirty(true)\n }\n\n /**\n * Normalizes a drag rectangle to have positive width and height.\n * @param dragRect The drag rectangle to normalize (modified in place)\n * @returns The normalized rectangle\n */\n #normalizeDragRect(dragRect: Rect): Rect {\n const w = Math.abs(dragRect[2])\n const h = Math.abs(dragRect[3])\n if (dragRect[2] < 0) dragRect[0] -= w\n if (dragRect[3] < 0) dragRect[1] -= h\n dragRect[2] = w\n dragRect[3] = h\n return dragRect\n }\n\n /**\n * Gets all positionable items that overlap with the given rectangle.\n * @param rect The rectangle to check against\n * @returns Set of positionable items that overlap with the rectangle\n */\n #getItemsInRect(rect: Rect): Set<Positionable> {\n const { graph, subgraph } = this\n if (!graph) throw new NullGraphError()\n\n const items = new Set<Positionable>()\n\n if (subgraph) {\n const { inputNode, outputNode } = subgraph\n if (overlapBounding(rect, inputNode.boundingRect)) items.add(inputNode)\n if (overlapBounding(rect, outputNode.boundingRect)) items.add(outputNode)\n }\n\n for (const node of graph._nodes) {\n if (overlapBounding(rect, node.boundingRect)) items.add(node)\n }\n\n // Check groups (must be wholly inside)\n for (const group of graph.groups) {\n if (containsRect(rect, group._bounding)) {\n group.recomputeInsideNodes()\n items.add(group)\n }\n }\n\n // Check reroutes (center point must be inside)\n for (const reroute of graph.reroutes.values()) {\n if (isPointInRect(reroute.pos, rect)) items.add(reroute)\n }\n\n return items\n }\n\n /**\n * Handles live selection updates during drag. Called on each pointer move.\n * @param e The pointer move event\n * @param dragRect The current drag rectangle\n * @param initialSelection The selection state before the drag started\n */\n private handleLiveSelect(\n e: CanvasPointerEvent,\n dragRect: Rect,\n initialSelection: Set<Positionable>\n ): void {\n // Ensure rect is current even if pointer.onDrag fires before processMouseMove updates it\n dragRect[2] = e.canvasX - dragRect[0]\n dragRect[3] = e.canvasY - dragRect[1]\n\n // Create a normalized copy for overlap checking\n const normalizedRect: Rect = [\n dragRect[0],\n dragRect[1],\n dragRect[2],\n dragRect[3]\n ]\n this.#normalizeDragRect(normalizedRect)\n\n const itemsInRect = this.#getItemsInRect(normalizedRect)\n\n const desired = new Set<Positionable>()\n if (e.shiftKey && !e.altKey) {\n for (const item of initialSelection) desired.add(item)\n for (const item of itemsInRect) desired.add(item)\n } else if (e.altKey && !e.shiftKey) {\n for (const item of initialSelection)\n if (!itemsInRect.has(item)) desired.add(item)\n } else {\n for (const item of itemsInRect) desired.add(item)\n }\n\n let changed = false\n for (const item of [...this.selectedItems]) {\n if (!desired.has(item)) {\n this.deselect(item)\n changed = true\n }\n }\n for (const item of desired) {\n if (!this.selectedItems.has(item)) {\n this.select(item)\n changed = true\n }\n }\n\n if (changed) {\n this.onSelectionChange?.(this.selected_nodes)\n this.setDirty(true)\n }\n }\n\n /**\n * Finalizes the live selection when drag ends.\n */\n private finalizeLiveSelect(): void {\n // Selection is already updated by handleLiveSelect\n // Just trigger the final selection change callback\n this.onSelectionChange?.(this.selected_nodes)\n }\n\n /**\n * Handles multi-select when drag ends (classic mode).\n * @param e The pointer up event\n * @param dragRect The drag rectangle\n */\n #handleMultiSelect(e: CanvasPointerEvent, dragRect: Rect): void {\n const normalizedRect: Rect = [\n dragRect[0],\n dragRect[1],\n dragRect[2],\n dragRect[3]\n ]\n this.#normalizeDragRect(normalizedRect)\n\n const itemsInRect = this.#getItemsInRect(normalizedRect)\n const { selectedItems } = this\n\n if (e.shiftKey) {\n // Add to selection\n for (const item of itemsInRect) this.select(item)\n } else if (e.altKey) {\n // Remove from selection\n for (const item of itemsInRect) this.deselect(item)\n } else {\n // Replace selection\n for (const item of selectedItems.values()) {\n if (!itemsInRect.has(item)) this.deselect(item)\n }\n for (const item of itemsInRect) this.select(item)\n }\n\n this.onSelectionChange?.(this.selected_nodes)\n }\n\n /**\n * Determines whether to select or deselect an item that has received a pointer event. Will deselect other nodes if\n * @param item Canvas item to select/deselect\n * @param e The MouseEvent to handle\n * @param sticky Prevents deselecting individual nodes (as used by aux/right-click)\n * @remarks\n * Accessibility: anyone using {@link mutli_select} always deselects when clicking empty space.\n */\n processSelect<TPositionable extends Positionable = LGraphNode>(\n item: TPositionable | null | undefined,\n e: CanvasPointerEvent | undefined,\n sticky: boolean = false\n ): void {\n const addModifier = e?.shiftKey\n const subtractModifier = e != null && (e.metaKey || e.ctrlKey)\n const eitherModifier = addModifier || subtractModifier\n const modifySelection = eitherModifier || this.multi_select\n\n if (!item) {\n if (!eitherModifier || this.multi_select) this.deselectAll()\n } else if (!item.selected || !this.selectedItems.has(item)) {\n if (!modifySelection) this.deselectAll(item)\n this.select(item)\n } else if (modifySelection && !sticky) {\n this.deselect(item)\n } else if (!sticky) {\n this.deselectAll(item)\n } else {\n return\n }\n this.onSelectionChange?.(this.selected_nodes)\n this.setDirty(true)\n }\n\n /**\n * Selects a {@link Positionable} item.\n * @param item The canvas item to add to the selection.\n */\n select<TPositionable extends Positionable = LGraphNode>(\n item: TPositionable\n ): void {\n if (item.selected && this.selectedItems.has(item)) return\n\n item.selected = true\n this.selectedItems.add(item)\n this.state.selectionChanged = true\n if (!(item instanceof LGraphNode)) return\n\n // Node-specific handling\n item.onSelected?.()\n this.selected_nodes[item.id] = item\n\n this.onNodeSelected?.(item)\n\n // Highlight links\n if (item.inputs) {\n for (const input of item.inputs) {\n if (input.link == null) continue\n this.highlighted_links[input.link] = true\n }\n }\n if (item.outputs) {\n for (const id of item.outputs.flatMap((x) => x.links)) {\n if (id == null) continue\n this.highlighted_links[id] = true\n }\n }\n }\n\n /**\n * Deselects a {@link Positionable} item.\n * @param item The canvas item to remove from the selection.\n */\n deselect<TPositionable extends Positionable = LGraphNode>(\n item: TPositionable\n ): void {\n if (!item.selected && !this.selectedItems.has(item)) return\n\n item.selected = false\n this.selectedItems.delete(item)\n this.state.selectionChanged = true\n if (!(item instanceof LGraphNode)) return\n\n // Node-specific handling\n item.onDeselected?.()\n delete this.selected_nodes[item.id]\n\n this.onNodeDeselected?.(item)\n\n // Should be moved to top of function, and throw if null\n const { graph } = this\n if (!graph) return\n\n // Clear link highlight\n if (item.inputs) {\n for (const input of item.inputs) {\n if (input.link == null) continue\n\n const node = LLink.getOriginNode(graph, input.link)\n if (node && this.selectedItems.has(node)) continue\n\n delete this.highlighted_links[input.link]\n }\n }\n if (item.outputs) {\n for (const id of item.outputs.flatMap((x) => x.links)) {\n if (id == null) continue\n\n const node = LLink.getTargetNode(graph, id)\n if (node && this.selectedItems.has(node)) continue\n\n delete this.highlighted_links[id]\n }\n }\n }\n\n /** @deprecated See {@link LGraphCanvas.processSelect} */\n processNodeSelected(item: LGraphNode, e: CanvasPointerEvent): void {\n this.processSelect(\n item,\n e,\n e && (e.shiftKey || e.metaKey || e.ctrlKey || this.multi_select)\n )\n }\n\n /** @deprecated See {@link LGraphCanvas.select} */\n selectNode(node: LGraphNode, add_to_current_selection?: boolean): void {\n if (node == null) {\n this.deselectAll()\n } else {\n this.selectNodes([node], add_to_current_selection)\n }\n }\n\n get empty(): boolean {\n if (!this.graph) throw new NullGraphError()\n return this.graph.empty\n }\n\n get positionableItems() {\n if (!this.graph) throw new NullGraphError()\n return this.graph.positionableItems()\n }\n\n /**\n * Selects several items.\n * @param items Items to select - if falsy, all items on the canvas will be selected\n * @param add_to_current_selection If set, the items will be added to the current selection instead of replacing it\n */\n selectItems(\n items?: Positionable[],\n add_to_current_selection?: boolean\n ): void {\n const itemsToSelect = items ?? this.positionableItems\n if (!add_to_current_selection) this.deselectAll()\n for (const item of itemsToSelect) this.select(item)\n this.onSelectionChange?.(this.selected_nodes)\n this.setDirty(true)\n }\n\n /**\n * selects several nodes (or adds them to the current selection)\n * @deprecated See {@link LGraphCanvas.selectItems}\n */\n selectNodes(nodes?: LGraphNode[], add_to_current_selection?: boolean): void {\n this.selectItems(nodes, add_to_current_selection)\n }\n\n /** @deprecated See {@link LGraphCanvas.deselect} */\n deselectNode(node: LGraphNode): void {\n this.deselect(node)\n }\n\n /**\n * Deselects all items on the canvas.\n * @param keepSelected If set, this item will not be removed from the selection.\n */\n deselectAll(keepSelected?: Positionable): void {\n if (!this.graph) return\n\n const selected = this.selectedItems\n if (!selected.size) return\n\n const initialSelectionSize = selected.size\n let wasSelected: Positionable | undefined\n for (const sel of selected) {\n if (sel === keepSelected) {\n wasSelected = sel\n continue\n }\n sel.onDeselected?.()\n sel.selected = false\n }\n selected.clear()\n if (wasSelected) selected.add(wasSelected)\n\n this.setDirty(true)\n\n // Legacy code\n const oldNode =\n keepSelected?.id == null ? null : this.selected_nodes[keepSelected.id]\n this.selected_nodes = {}\n this.current_node = null\n this.highlighted_links = {}\n\n if (keepSelected instanceof LGraphNode) {\n // Handle old object lookup\n if (oldNode) this.selected_nodes[oldNode.id] = oldNode\n\n // Highlight links\n if (keepSelected.inputs) {\n for (const input of keepSelected.inputs) {\n if (input.link == null) continue\n this.highlighted_links[input.link] = true\n }\n }\n if (keepSelected.outputs) {\n for (const id of keepSelected.outputs.flatMap((x) => x.links)) {\n if (id == null) continue\n this.highlighted_links[id] = true\n }\n }\n }\n\n // Only set selectionChanged if selection actually changed\n const finalSelectionSize = selected.size\n if (initialSelectionSize !== finalSelectionSize) {\n this.state.selectionChanged = true\n this.onSelectionChange?.(this.selected_nodes)\n }\n }\n\n /** @deprecated See {@link LGraphCanvas.deselectAll} */\n deselectAllNodes(): void {\n this.deselectAll()\n }\n\n /**\n * Deletes all selected items from the graph.\n * @todo Refactor deletion task to LGraph. Selection is a canvas property, delete is a graph action.\n */\n deleteSelected(): void {\n const { graph } = this\n if (!graph) throw new NullGraphError()\n\n this.emitBeforeChange()\n graph.beforeChange()\n\n for (const item of this.selectedItems) {\n if (item instanceof LGraphNode) {\n const node = item\n if (node.block_delete) continue\n node.connectInputToOutput()\n graph.remove(node)\n this.onNodeDeselected?.(node)\n } else if (item instanceof LGraphGroup) {\n graph.remove(item)\n } else if (item instanceof Reroute) {\n graph.removeReroute(item.id)\n }\n }\n\n this.selected_nodes = {}\n this.selectedItems.clear()\n this.current_node = null\n this.highlighted_links = {}\n\n this.state.selectionChanged = true\n this.onSelectionChange?.(this.selected_nodes)\n this.setDirty(true)\n graph.afterChange()\n this.emitAfterChange()\n }\n\n /**\n * deletes all nodes in the current selection from the graph\n * @deprecated See {@link LGraphCanvas.deleteSelected}\n */\n deleteSelectedNodes(): void {\n this.deleteSelected()\n }\n\n /**\n * centers the camera on a given node\n */\n centerOnNode(node: LGraphNode): void {\n const dpi = window?.devicePixelRatio || 1\n this.ds.offset[0] =\n -node.pos[0] -\n node.size[0] * 0.5 +\n (this.canvas.width * 0.5) / (this.ds.scale * dpi)\n this.ds.offset[1] =\n -node.pos[1] -\n node.size[1] * 0.5 +\n (this.canvas.height * 0.5) / (this.ds.scale * dpi)\n this.setDirty(true, true)\n }\n\n /**\n * adds some useful properties to a mouse event, like the position in graph coordinates\n */\n adjustMouseEvent<T extends MouseEvent>(\n e: T & Partial<CanvasPointerExtensions>\n ): asserts e is T & CanvasPointerEvent {\n let clientX_rel = e.clientX\n let clientY_rel = e.clientY\n\n if (this.canvas) {\n const b = this.canvas.getBoundingClientRect()\n clientX_rel -= b.left\n clientY_rel -= b.top\n }\n\n e.safeOffsetX = clientX_rel\n e.safeOffsetY = clientY_rel\n\n // TODO: Find a less brittle way to do this\n\n // Only set deltaX and deltaY if not already set.\n // If deltaX and deltaY are already present, they are read-only.\n // Setting them would result browser error => zoom in/out feature broken.\n if (e.deltaX === undefined)\n e.deltaX = clientX_rel - this.last_mouse_position[0]\n if (e.deltaY === undefined)\n e.deltaY = clientY_rel - this.last_mouse_position[1]\n\n this.last_mouse_position[0] = clientX_rel\n this.last_mouse_position[1] = clientY_rel\n\n e.canvasX = clientX_rel / this.ds.scale - this.ds.offset[0]\n e.canvasY = clientY_rel / this.ds.scale - this.ds.offset[1]\n }\n\n /**\n * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom\n */\n setZoom(value: number, zooming_center: Point) {\n this.ds.changeScale(value, zooming_center)\n this.#dirty()\n }\n\n /**\n * converts a coordinate from graph coordinates to canvas2D coordinates\n */\n convertOffsetToCanvas(pos: Point, _out?: Point): Point {\n return this.ds.convertOffsetToCanvas(pos)\n }\n\n /**\n * converts a coordinate from Canvas2D coordinates to graph space\n */\n convertCanvasToOffset(pos: Point, out?: Point): Point {\n return this.ds.convertCanvasToOffset(pos, out)\n }\n\n // converts event coordinates from canvas2D to graph coordinates\n convertEventToCanvasOffset(e: MouseEvent): Point {\n const rect = this.canvas.getBoundingClientRect()\n // TODO: -> this.ds.convertCanvasToOffset\n return this.convertCanvasToOffset([\n e.clientX - rect.left,\n e.clientY - rect.top\n ])\n }\n\n /**\n * brings a node to front (above all other nodes)\n */\n bringToFront(node: LGraphNode): void {\n const { graph } = this\n if (!graph) throw new NullGraphError()\n\n const i = graph._nodes.indexOf(node)\n if (i == -1) return\n\n graph._nodes.splice(i, 1)\n graph._nodes.push(node)\n }\n\n /**\n * sends a node to the back (below all other nodes)\n */\n sendToBack(node: LGraphNode): void {\n const { graph } = this\n if (!graph) throw new NullGraphError()\n\n const i = graph._nodes.indexOf(node)\n if (i == -1) return\n\n graph._nodes.splice(i, 1)\n graph._nodes.unshift(node)\n }\n\n /**\n * Determines which nodes are visible and populates {@link out} with the results.\n * @param nodes The list of nodes to check - if falsy, all nodes in the graph will be checked\n * @param out Array to write visible nodes into - if falsy, a new array is created instead\n * @returns Array passed ({@link out}), or a new array containing all visible nodes\n */\n computeVisibleNodes(nodes?: LGraphNode[], out?: LGraphNode[]): LGraphNode[] {\n const visible_nodes = out || []\n visible_nodes.length = 0\n if (!this.graph) throw new NullGraphError()\n\n const _nodes = nodes || this.graph._nodes\n for (const node of _nodes) {\n node.updateArea(this.ctx)\n // Not in visible area\n if (!overlapBounding(this.visible_area, node.renderArea)) continue\n\n visible_nodes.push(node)\n }\n return visible_nodes\n }\n\n /**\n * Checks if a node is visible on the canvas.\n * @param node The node to check\n * @returns `true` if the node is visible, otherwise `false`\n */\n isNodeVisible(node: LGraphNode): boolean {\n return this.#visible_node_ids.has(node.id)\n }\n\n /**\n * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes)\n */\n draw(force_canvas?: boolean, force_bgcanvas?: boolean): void {\n if (!this.canvas || this.canvas.width == 0 || this.canvas.height == 0)\n return\n\n // fps counting\n const now = LiteGraph.getTime()\n this.render_time = (now - this.last_draw_time) * 0.001\n this.last_draw_time = now\n\n if (this.graph) this.ds.computeVisibleArea(this.viewport)\n\n // Compute node size before drawing links.\n if (this.dirty_canvas || force_canvas) {\n this.computeVisibleNodes(undefined, this.visible_nodes)\n // Update visible node IDs\n this.#visible_node_ids = new Set(\n this.visible_nodes.map((node) => node.id)\n )\n\n // Arrange subgraph IO nodes\n const { subgraph } = this\n if (subgraph) {\n subgraph.inputNode.arrange()\n subgraph.outputNode.arrange()\n }\n }\n\n if (\n this.dirty_bgcanvas ||\n force_bgcanvas ||\n this.always_render_background ||\n (this.graph?._last_trigger_time &&\n now - this.graph._last_trigger_time < 1000)\n ) {\n this.drawBackCanvas()\n }\n\n if (this.dirty_canvas || force_canvas) this.drawFrontCanvas()\n\n this.fps = this.render_time ? 1.0 / this.render_time : 0\n this.frame++\n }\n\n /**\n * draws the front canvas (the one containing all the nodes)\n */\n drawFrontCanvas(): void {\n this.dirty_canvas = false\n\n const { ctx, canvas, graph, linkConnector } = this\n\n // @ts-expect-error start2D method not in standard CanvasRenderingContext2D\n if (ctx.start2D && !this.viewport) {\n // @ts-expect-error start2D method not in standard CanvasRenderingContext2D\n ctx.start2D()\n ctx.restore()\n ctx.setTransform(1, 0, 0, 1, 0, 0)\n }\n\n // clip dirty area if there is one, otherwise work in full canvas\n const area = this.viewport || this.dirty_area\n if (area) {\n ctx.save()\n ctx.beginPath()\n ctx.rect(area[0], area[1], area[2], area[3])\n ctx.clip()\n }\n\n // TODO: Set snapping value when changed instead of once per frame\n this.#snapToGrid =\n this.#shiftDown || LiteGraph.alwaysSnapToGrid\n ? this.graph?.getSnapToGridSize()\n : undefined\n\n // clear\n // canvas.width = canvas.width;\n if (this.clear_background) {\n if (area) ctx.clearRect(area[0], area[1], area[2], area[3])\n else ctx.clearRect(0, 0, canvas.width, canvas.height)\n }\n\n // draw bg canvas\n if (this.bgcanvas == this.canvas) {\n this.drawBackCanvas()\n } else {\n const scale = window.devicePixelRatio\n ctx.drawImage(\n this.bgcanvas,\n 0,\n 0,\n this.bgcanvas.width / scale,\n this.bgcanvas.height / scale\n )\n }\n\n // rendering\n this.onRender?.(canvas, ctx)\n\n // info widget\n if (this.show_info) {\n const pos = this.fpsInfoLocation ?? area\n this.renderInfo(ctx, pos?.[0] ?? 0, pos?.[1] ?? 0)\n }\n\n if (graph) {\n // apply transformations\n ctx.save()\n this.ds.toCanvasContext(ctx)\n\n // draw nodes\n const { visible_nodes } = this\n const drawSnapGuides =\n this.#snapToGrid &&\n (this.isDragging || layoutStore.isDraggingVueNodes.value)\n\n for (const node of visible_nodes) {\n ctx.save()\n\n // Draw snap shadow\n if (drawSnapGuides && this.selectedItems.has(node))\n this.drawSnapGuide(ctx, node)\n\n // Localise co-ordinates to node position\n ctx.translate(node.pos[0], node.pos[1])\n\n // Draw\n this.drawNode(node, ctx)\n\n ctx.restore()\n }\n\n // Draw subgraph IO nodes\n this.subgraph?.draw(\n ctx,\n this.colourGetter,\n this.linkConnector.renderLinks[0]?.fromSlot,\n this.editor_alpha\n )\n\n // on top (debug)\n if (this.render_execution_order) {\n this.drawExecutionOrder(ctx)\n }\n\n // connections ontop?\n if (graph.config.links_ontop) {\n this.drawConnections(ctx)\n }\n\n if (linkConnector.isConnecting) {\n // current connection (the one being dragged by the mouse)\n const { renderLinks } = linkConnector\n const highlightPos = this.#getHighlightPosition()\n ctx.lineWidth = this.connections_width\n\n for (const renderLink of renderLinks) {\n const {\n fromSlot,\n fromPos: pos,\n fromDirection,\n dragDirection\n } = renderLink\n const connShape = fromSlot.shape\n const connType = fromSlot.type\n\n const colour = resolveConnectingLinkColor(connType)\n\n // the connection being dragged by the mouse\n if (this.linkRenderer) {\n this.linkRenderer.renderDraggingLink(\n ctx,\n pos,\n highlightPos,\n colour,\n fromDirection,\n dragDirection,\n {\n ...this.buildLinkRenderContext(),\n linkMarkerShape: LinkMarkerShape.None\n }\n )\n }\n\n ctx.fillStyle = colour\n ctx.beginPath()\n if (connType === LiteGraph.EVENT || connShape === RenderShape.BOX) {\n ctx.rect(pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, 10)\n ctx.rect(\n highlightPos[0] - 6 + 0.5,\n highlightPos[1] - 5 + 0.5,\n 14,\n 10\n )\n } else if (connShape === RenderShape.ARROW) {\n ctx.moveTo(pos[0] + 8, pos[1] + 0.5)\n ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5)\n ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5)\n ctx.closePath()\n } else {\n ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2)\n ctx.arc(highlightPos[0], highlightPos[1], 4, 0, Math.PI * 2)\n }\n ctx.fill()\n }\n\n // Gradient half-border over target node\n this.#renderSnapHighlight(ctx, highlightPos)\n }\n\n // on top of link center\n if (\n !this.isDragging &&\n this.over_link_center &&\n this.render_link_tooltip\n ) {\n this.drawLinkTooltip(ctx, this.over_link_center)\n } else {\n this.onDrawLinkTooltip?.(ctx, null)\n }\n\n // custom info\n this.onDrawForeground?.(ctx, this.visible_area)\n\n ctx.restore()\n }\n\n this.onDrawOverlay?.(ctx)\n\n if (area) ctx.restore()\n }\n\n /** @returns If the pointer is over a link centre marker, the link segment it belongs to. Otherwise, `undefined`. */\n #getLinkCentreOnPos(e: CanvasPointerEvent): LinkSegment | undefined {\n // Skip hit detection if center markers are disabled\n if (this.linkMarkerShape === LinkMarkerShape.None) {\n return undefined\n }\n\n for (const linkSegment of this.renderedPaths) {\n const centre = linkSegment._pos\n if (!centre) continue\n\n if (\n isInRectangle(e.canvasX, e.canvasY, centre[0] - 4, centre[1] - 4, 8, 8)\n ) {\n return linkSegment\n }\n }\n }\n\n /** Get the target snap / highlight point in graph space */\n #getHighlightPosition(): Readonly<Point> {\n return LiteGraph.snaps_for_comfy\n ? (this.linkConnector.state.snapLinksPos ??\n this._highlight_pos ??\n this.graph_mouse)\n : this.graph_mouse\n }\n\n /**\n * Renders indicators showing where a link will connect if released.\n * Partial border over target node and a highlight over the slot itself.\n * @param ctx Canvas 2D context\n */\n #renderSnapHighlight(\n ctx: CanvasRenderingContext2D,\n highlightPos: Readonly<Point>\n ): void {\n const linkConnectorSnap = !!this.linkConnector.state.snapLinksPos\n if (!this._highlight_pos && !linkConnectorSnap) return\n\n ctx.fillStyle = '#ffcc00'\n ctx.beginPath()\n const shape = this._highlight_input?.shape\n\n if (shape === RenderShape.ARROW) {\n ctx.moveTo(highlightPos[0] + 8, highlightPos[1] + 0.5)\n ctx.lineTo(highlightPos[0] - 4, highlightPos[1] + 6 + 0.5)\n ctx.lineTo(highlightPos[0] - 4, highlightPos[1] - 6 + 0.5)\n ctx.closePath()\n } else {\n ctx.arc(highlightPos[0], highlightPos[1], 6, 0, Math.PI * 2)\n }\n ctx.fill()\n\n const { linkConnector } = this\n const { overReroute, overWidget } = linkConnector\n if (\n !LiteGraph.snap_highlights_node ||\n !linkConnector.isConnecting ||\n linkConnectorSnap\n )\n return\n\n // Reroute highlight\n overReroute?.drawHighlight(ctx, '#ffcc00aa')\n\n // Ensure we're mousing over a node and connecting a link\n const node = this.node_over\n if (!node) return\n\n const { strokeStyle, lineWidth } = ctx\n\n const area = node.boundingRect\n const gap = 3\n const radius = LiteGraph.ROUND_RADIUS + gap\n\n const x = area[0] - gap\n const y = area[1] - gap\n const width = area[2] + gap * 2\n const height = area[3] + gap * 2\n\n ctx.beginPath()\n ctx.roundRect(x, y, width, height, radius)\n\n // TODO: Currently works on LTR slots only. Add support for other directions.\n const start = linkConnector.state.connectingTo === 'output' ? 0 : 1\n const inverter = start ? -1 : 1\n\n // Radial highlight centred on highlight pos\n const hx = highlightPos[0]\n const hy = highlightPos[1]\n const gRadius =\n width < height ? width : width * Math.max(height / width, 0.5)\n\n const gradient = ctx.createRadialGradient(hx, hy, 0, hx, hy, gRadius)\n gradient.addColorStop(1, '#00000000')\n gradient.addColorStop(0, '#ffcc00aa')\n\n // Linear gradient over half the node.\n const linearGradient = ctx.createLinearGradient(x, y, x + width, y)\n linearGradient.addColorStop(0.5, '#00000000')\n linearGradient.addColorStop(start + 0.67 * inverter, '#ddeeff33')\n linearGradient.addColorStop(start + inverter, '#ffcc0055')\n\n /**\n * Workaround for a canvas render issue.\n * In Chromium 129 (2024-10-15), rounded corners can be rendered with the wrong part of a gradient colour.\n * Occurs only at certain thicknesses / arc sizes.\n */\n ctx.setLineDash([radius, radius * 0.001])\n\n ctx.lineWidth = 1\n ctx.strokeStyle = linearGradient\n ctx.stroke()\n\n if (overWidget) {\n const { computedHeight } = overWidget\n\n ctx.beginPath()\n const {\n pos: [nodeX, nodeY]\n } = node\n const height = LiteGraph.NODE_WIDGET_HEIGHT\n if (\n overWidget.type.startsWith('custom') &&\n computedHeight != null &&\n computedHeight > height * 2\n ) {\n // Most likely DOM widget text box\n ctx.rect(\n nodeX + 9,\n nodeY + overWidget.y + 9,\n (overWidget.width ?? area[2]) - 18,\n computedHeight - 18\n )\n } else {\n // Regular widget, probably\n ctx.roundRect(\n nodeX + BaseWidget.margin,\n nodeY + overWidget.y,\n overWidget.width ?? area[2],\n height,\n height * 0.5\n )\n }\n ctx.stroke()\n }\n\n ctx.strokeStyle = gradient\n ctx.stroke()\n\n ctx.setLineDash([])\n ctx.lineWidth = lineWidth\n ctx.strokeStyle = strokeStyle\n }\n\n /**\n * draws some useful stats in the corner of the canvas\n */\n renderInfo(ctx: CanvasRenderingContext2D, x: number, y: number): void {\n x = x || 10\n y = y || this.canvas.offsetHeight - 80\n\n ctx.save()\n ctx.translate(x, y)\n\n ctx.font = `10px ${LiteGraph.DEFAULT_FONT}`\n ctx.fillStyle = '#888'\n ctx.textAlign = 'left'\n if (this.graph) {\n ctx.fillText(`T: ${this.graph.globaltime.toFixed(2)}s`, 5, 13 * 1)\n ctx.fillText(`I: ${this.graph.iteration}`, 5, 13 * 2)\n ctx.fillText(\n `N: ${this.graph._nodes.length} [${this.visible_nodes.length}]`,\n 5,\n 13 * 3\n )\n ctx.fillText(`V: ${this.graph._version}`, 5, 13 * 4)\n ctx.fillText(`FPS:${this.fps.toFixed(2)}`, 5, 13 * 5)\n } else {\n ctx.fillText('No graph selected', 5, 13 * 1)\n }\n ctx.restore()\n }\n\n /**\n * draws the back canvas (the one containing the background and the connections)\n */\n drawBackCanvas(): void {\n const canvas = this.bgcanvas\n if (\n canvas.width != this.canvas.width ||\n canvas.height != this.canvas.height\n ) {\n canvas.width = this.canvas.width\n canvas.height = this.canvas.height\n }\n\n if (!this.bgctx) {\n this.bgctx = this.bgcanvas.getContext('2d')\n }\n const ctx = this.bgctx\n if (!ctx) throw new TypeError('Background canvas context was null.')\n\n const viewport = this.viewport || [\n 0,\n 0,\n ctx.canvas.width,\n ctx.canvas.height\n ]\n\n // clear\n if (this.clear_background) {\n ctx.clearRect(viewport[0], viewport[1], viewport[2], viewport[3])\n }\n\n const bg_already_painted = this.onRenderBackground\n ? this.onRenderBackground(canvas, ctx)\n : false\n\n // reset in case of error\n if (!this.viewport) {\n const scale = window.devicePixelRatio\n ctx.restore()\n ctx.setTransform(scale, 0, 0, scale, 0, 0)\n }\n\n if (this.graph) {\n // apply transformations\n ctx.save()\n this.ds.toCanvasContext(ctx)\n\n // render BG\n if (\n this.ds.scale < 1.5 &&\n !bg_already_painted &&\n this.clear_background_color\n ) {\n ctx.fillStyle = this.clear_background_color\n ctx.fillRect(\n this.visible_area[0],\n this.visible_area[1],\n this.visible_area[2],\n this.visible_area[3]\n )\n }\n\n if (this.background_image && this.ds.scale > 0.5 && !bg_already_painted) {\n if (this.zoom_modify_alpha) {\n ctx.globalAlpha = (1.0 - 0.5 / this.ds.scale) * this.editor_alpha\n } else {\n ctx.globalAlpha = this.editor_alpha\n }\n ctx.imageSmoothingEnabled = false\n if (!this._bg_img || this._bg_img.name != this.background_image) {\n this._bg_img = new Image()\n this._bg_img.name = this.background_image\n this._bg_img.src = this.background_image\n this._bg_img.addEventListener('load', () => {\n this.draw(true, true)\n })\n }\n\n let pattern = this._pattern\n if (pattern == null && this._bg_img.width > 0) {\n pattern = ctx.createPattern(this._bg_img, 'repeat') ?? undefined\n this._pattern_img = this._bg_img\n this._pattern = pattern\n }\n\n // NOTE: This ridiculous kludge provides a significant performance increase when rendering many large (> canvas width) paths in HTML canvas.\n // I could find no documentation or explanation. Requires that the BG image is set.\n if (pattern) {\n ctx.fillStyle = pattern\n ctx.fillRect(\n this.visible_area[0],\n this.visible_area[1],\n this.visible_area[2],\n this.visible_area[3]\n )\n ctx.fillStyle = 'transparent'\n }\n\n ctx.globalAlpha = 1.0\n ctx.imageSmoothingEnabled = true\n }\n if (this.bg_tint) {\n ctx.fillStyle = this.bg_tint\n ctx.fillRect(\n this.visible_area[0],\n this.visible_area[1],\n this.visible_area[2],\n this.visible_area[3]\n )\n ctx.fillStyle = 'transparent'\n }\n\n // groups\n if (this.graph._groups.length) {\n this.drawGroups(canvas, ctx)\n }\n\n this.onDrawBackground?.(ctx, this.visible_area)\n\n // DEBUG: show clipping area\n // ctx.fillStyle = \"red\";\n // ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20);\n // bg\n if (this.render_canvas_border) {\n ctx.strokeStyle = '#235'\n ctx.strokeRect(0, 0, canvas.width, canvas.height)\n }\n\n if (this.render_connections_shadows) {\n ctx.shadowColor = '#000'\n ctx.shadowOffsetX = 0\n ctx.shadowOffsetY = 0\n ctx.shadowBlur = 6\n } else {\n ctx.shadowColor = 'rgba(0,0,0,0)'\n }\n\n // draw connections\n this.drawConnections(ctx)\n\n ctx.shadowColor = 'rgba(0,0,0,0)'\n\n // restore state\n ctx.restore()\n }\n\n this.dirty_bgcanvas = false\n // Forces repaint of the front canvas.\n this.dirty_canvas = true\n }\n\n /**\n * draws the given node inside the canvas\n */\n drawNode(node: LGraphNode, ctx: CanvasRenderingContext2D): void {\n this.current_node = node\n\n // When Vue nodes mode is enabled, LiteGraph should not draw node chrome or widgets.\n // We still need to keep slot metrics and layout in sync for hit-testing and links.\n // Interaction system changes coming later, chances are vue nodes mode will be mostly broken on land\n if (LiteGraph.vueNodesMode) {\n // Prepare concrete slots and compute layout measures without rendering visuals.\n node._setConcreteSlots()\n if (!node.collapsed) {\n node.arrange()\n }\n // Skip all node body/widget/title rendering. Vue overlay handles visuals.\n return\n }\n\n const color = node.renderingColor\n const bgcolor = node.renderingBgColor\n\n ctx.globalAlpha = this.getNodeModeAlpha(node)\n\n if (this.render_shadows && !this.low_quality) {\n ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR\n ctx.shadowOffsetX = 2 * this.ds.scale\n ctx.shadowOffsetY = 2 * this.ds.scale\n ctx.shadowBlur = 3 * this.ds.scale\n } else {\n ctx.shadowColor = 'transparent'\n }\n\n // custom draw collapsed method (draw after shadows because they are affected)\n if (node.flags.collapsed && node.onDrawCollapsed?.(ctx, this) == true)\n return\n\n // clip if required (mask)\n const shape = node._shape || RenderShape.BOX\n const size = temp_vec2\n size[0] = node.renderingSize[0]\n size[1] = node.renderingSize[1]\n\n if (node.collapsed) {\n ctx.font = this.inner_text_font\n }\n\n if (node.clip_area) {\n // Start clipping\n ctx.save()\n ctx.beginPath()\n if (shape == RenderShape.BOX) {\n ctx.rect(0, 0, size[0], size[1])\n } else if (shape == RenderShape.ROUND) {\n ctx.roundRect(0, 0, size[0], size[1], [10])\n } else if (shape == RenderShape.CIRCLE) {\n ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5, 0, Math.PI * 2)\n }\n ctx.clip()\n }\n\n // draw shape\n this.drawNodeShape(node, ctx, size, color, bgcolor, !!node.selected)\n\n // Render title buttons (if not collapsed)\n if (node.title_buttons && !node.flags.collapsed) {\n const title_height = LiteGraph.NODE_TITLE_HEIGHT\n let current_x = size[0] // Start flush with right edge\n\n for (let i = 0; i < node.title_buttons.length; i++) {\n const button = node.title_buttons[i]\n if (!button.visible) {\n continue\n }\n\n const button_width = button.getWidth(ctx)\n current_x -= button_width\n\n // Center button vertically in title bar\n const button_y = -title_height + (title_height - button.height) / 2\n\n button.draw(ctx, current_x, button_y)\n current_x -= 2\n }\n }\n\n if (!this.low_quality) {\n node.drawBadges(ctx)\n }\n\n ctx.shadowColor = 'transparent'\n\n // TODO: Legacy behaviour: onDrawForeground received ctx in this state\n ctx.strokeStyle = LiteGraph.NODE_BOX_OUTLINE_COLOR\n\n // Draw Foreground\n node.onDrawForeground?.(ctx, this, this.canvas)\n\n // connection slots\n ctx.font = this.inner_text_font\n\n // render inputs and outputs\n node._setConcreteSlots()\n if (!node.collapsed) {\n node.arrange()\n node.drawSlots(ctx, {\n fromSlot: this.linkConnector.renderLinks[0]?.fromSlot as\n | INodeOutputSlot\n | INodeInputSlot,\n colorContext: this.colourGetter,\n editorAlpha: this.editor_alpha,\n lowQuality: this.low_quality\n })\n\n ctx.textAlign = 'left'\n ctx.globalAlpha = 1\n\n this.drawNodeWidgets(node, null, ctx)\n } else if (this.render_collapsed_slots) {\n node.drawCollapsedSlots(ctx)\n }\n\n if (node.clip_area) {\n ctx.restore()\n }\n\n ctx.globalAlpha = 1.0\n }\n\n /**\n * Draws the link mouseover effect and tooltip.\n * @param ctx Canvas 2D context to draw on\n * @param link The link to render the mouseover effect for\n * @remarks\n * Called against {@link LGraphCanvas.over_link_center}.\n * @todo Split tooltip from hover, so it can be drawn / eased separately\n */\n drawLinkTooltip(ctx: CanvasRenderingContext2D, link: LinkSegment): void {\n const pos = link._pos\n ctx.fillStyle = 'black'\n ctx.beginPath()\n if (this.linkMarkerShape === LinkMarkerShape.Arrow) {\n const transform = ctx.getTransform()\n ctx.translate(pos[0], pos[1])\n // Assertion: Number.isFinite guarantees this is a number.\n if (Number.isFinite(link._centreAngle))\n ctx.rotate(link._centreAngle as number)\n ctx.moveTo(-2, -3)\n ctx.lineTo(+4, 0)\n ctx.lineTo(-2, +3)\n ctx.setTransform(transform)\n } else if (\n this.linkMarkerShape == null ||\n this.linkMarkerShape === LinkMarkerShape.Circle\n ) {\n ctx.arc(pos[0], pos[1], 3, 0, Math.PI * 2)\n }\n ctx.fill()\n\n // @ts-expect-error TODO: Better value typing\n const { data } = link\n if (data == null) return\n\n // @ts-expect-error TODO: Better value typing\n if (this.onDrawLinkTooltip?.(ctx, link, this) == true) return\n\n let text: string | null = null\n\n if (typeof data === 'number') text = data.toFixed(2)\n else if (typeof data === 'string') text = `\"${data}\"`\n else if (typeof data === 'boolean') text = String(data)\n else if (data.toToolTip) text = data.toToolTip()\n else text = `[${data.constructor.name}]`\n\n if (text == null) return\n\n // Hard-coded tooltip limit\n text = text.substring(0, 30)\n\n ctx.font = '14px Courier New'\n const info = ctx.measureText(text)\n const w = info.width + 20\n const h = 24\n ctx.shadowColor = 'black'\n ctx.shadowOffsetX = 2\n ctx.shadowOffsetY = 2\n ctx.shadowBlur = 3\n ctx.fillStyle = '#454'\n ctx.beginPath()\n ctx.roundRect(pos[0] - w * 0.5, pos[1] - 15 - h, w, h, [3])\n ctx.moveTo(pos[0] - 10, pos[1] - 15)\n ctx.lineTo(pos[0] + 10, pos[1] - 15)\n ctx.lineTo(pos[0], pos[1] - 5)\n ctx.fill()\n ctx.shadowColor = 'transparent'\n ctx.textAlign = 'center'\n ctx.fillStyle = '#CEC'\n ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3)\n }\n\n /**\n * Draws the shape of the given node on the canvas\n * @param node The node to draw\n * @param ctx 2D canvas rendering context used to draw\n * @param size Size of the background to draw, in graph units. Differs from node size if collapsed, etc.\n * @param fgcolor Foreground colour - used for text\n * @param bgcolor Background colour of the node\n * @param _selected Whether to render the node as selected. Likely to be removed in future, as current usage is simply the selected property of the node.\n */\n drawNodeShape(\n node: LGraphNode,\n ctx: CanvasRenderingContext2D,\n size: Size,\n fgcolor: CanvasColour,\n bgcolor: CanvasColour,\n _selected: boolean\n ): void {\n // Rendering options\n ctx.strokeStyle = fgcolor\n ctx.fillStyle = bgcolor\n\n const title_height = LiteGraph.NODE_TITLE_HEIGHT\n const { low_quality } = this\n\n const { collapsed } = node.flags\n const shape = node.renderingShape\n const { title_mode } = node\n\n const render_title =\n title_mode == TitleMode.TRANSPARENT_TITLE ||\n title_mode == TitleMode.NO_TITLE\n ? false\n : true\n\n // Normalised node dimensions\n const area = tmp_area\n area.set(node.boundingRect)\n area[0] -= node.pos[0]\n area[1] -= node.pos[1]\n\n const old_alpha = ctx.globalAlpha\n\n // Draw node background (shape)\n ctx.beginPath()\n if (shape == RenderShape.BOX || low_quality) {\n ctx.rect(area[0], area[1], area[2], area[3])\n } else if (shape == RenderShape.ROUND || shape == RenderShape.CARD) {\n ctx.roundRect(\n area[0],\n area[1],\n area[2],\n area[3],\n shape == RenderShape.CARD\n ? [LiteGraph.ROUND_RADIUS, LiteGraph.ROUND_RADIUS, 0, 0]\n : [LiteGraph.ROUND_RADIUS]\n )\n } else if (shape == RenderShape.CIRCLE) {\n ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5, 0, Math.PI * 2)\n }\n ctx.fill()\n\n // Separator - title bar <-> body\n if (!collapsed && render_title) {\n ctx.shadowColor = 'transparent'\n ctx.fillStyle = 'rgba(0,0,0,0.2)'\n ctx.fillRect(0, -1, area[2], 2)\n }\n ctx.shadowColor = 'transparent'\n\n node.onDrawBackground?.(ctx)\n\n // Title bar background (remember, it is rendered ABOVE the node)\n if (render_title || title_mode == TitleMode.TRANSPARENT_TITLE) {\n node.drawTitleBarBackground(ctx, {\n scale: this.ds.scale,\n low_quality\n })\n\n // title box\n node.drawTitleBox(ctx, {\n scale: this.ds.scale,\n low_quality,\n box_size: 10\n })\n\n ctx.globalAlpha = old_alpha\n\n // title text\n node.drawTitleText(ctx, {\n scale: this.ds.scale,\n default_title_color: this.node_title_color,\n low_quality\n })\n\n // custom title render\n node.onDrawTitle?.(ctx)\n }\n\n // Draw stroke styles\n for (const getStyle of Object.values(node.strokeStyles)) {\n const strokeStyle = getStyle.call(node)\n if (strokeStyle) {\n strokeShape(ctx, area, {\n shape,\n title_height,\n title_mode,\n collapsed,\n ...strokeStyle\n })\n }\n }\n\n node.drawProgressBar(ctx)\n\n // these counter helps in conditioning drawing based on if the node has been executed or an action occurred\n if (node.execute_triggered != null && node.execute_triggered > 0)\n node.execute_triggered--\n if (node.action_triggered != null && node.action_triggered > 0)\n node.action_triggered--\n }\n\n /**\n * Draws a snap guide for a {@link Positionable} item.\n *\n * Initial design was a simple white rectangle representing the location the\n * item would land if dropped.\n * @param ctx The 2D canvas context to draw on\n * @param item The item to draw a snap guide for\n * @param shape The shape of the snap guide to draw\n * @todo Update to align snapping with boundingRect\n * @todo Shapes\n */\n drawSnapGuide(\n ctx: CanvasRenderingContext2D,\n item: Positionable,\n shape = RenderShape.ROUND\n ) {\n const snapGuide = temp\n snapGuide.set(item.boundingRect)\n\n // Not all items have pos equal to top-left of bounds\n const { pos } = item\n const offsetX = pos[0] - snapGuide[0]\n const offsetY = pos[1] - snapGuide[1]\n\n // Normalise boundingRect to pos to snap\n snapGuide[0] += offsetX\n snapGuide[1] += offsetY\n if (this.#snapToGrid) snapPoint(snapGuide, this.#snapToGrid)\n snapGuide[0] -= offsetX\n snapGuide[1] -= offsetY\n\n const { globalAlpha } = ctx\n ctx.globalAlpha = 1\n ctx.beginPath()\n const [x, y, w, h] = snapGuide\n if (shape === RenderShape.CIRCLE) {\n const midX = x + w * 0.5\n const midY = y + h * 0.5\n const radius = Math.min(w * 0.5, h * 0.5)\n ctx.arc(midX, midY, radius, 0, Math.PI * 2)\n } else {\n ctx.rect(x, y, w, h)\n }\n\n ctx.lineWidth = 0.5\n ctx.strokeStyle = '#FFFFFF66'\n ctx.fillStyle = '#FFFFFF22'\n ctx.fill()\n ctx.stroke()\n ctx.globalAlpha = globalAlpha\n }\n\n drawConnections(ctx: CanvasRenderingContext2D): void {\n this.renderedPaths.clear()\n if (this.links_render_mode === LinkRenderType.HIDDEN_LINK) return\n\n const { graph, subgraph } = this\n if (!graph) throw new NullGraphError()\n\n const visibleReroutes: Reroute[] = []\n\n const now = LiteGraph.getTime()\n const { visible_area } = this\n margin_area[0] = visible_area[0] - 20\n margin_area[1] = visible_area[1] - 20\n margin_area[2] = visible_area[2] + 40\n margin_area[3] = visible_area[3] + 40\n\n // draw connections\n ctx.lineWidth = this.connections_width\n\n ctx.fillStyle = '#AAA'\n ctx.strokeStyle = '#AAA'\n ctx.globalAlpha = this.editor_alpha\n // for every node\n const nodes = graph._nodes\n for (const node of nodes) {\n // for every input (we render just inputs because it is easier as every slot can only have one input)\n const { inputs } = node\n if (!inputs?.length) continue\n\n for (const [i, input] of inputs.entries()) {\n if (!input || input.link == null) continue\n\n const link_id = input.link\n const link = graph._links.get(link_id)\n if (!link) continue\n\n const endPos: Point = LiteGraph.vueNodesMode // TODO: still use LG get pos if vue nodes is off until stable\n ? getSlotPosition(node, i, true)\n : node.getInputPos(i)\n\n // find link info\n const start_node = graph.getNodeById(link.origin_id)\n if (start_node == null) continue\n\n const outputId = link.origin_slot\n const startPos: Point =\n outputId === -1\n ? [start_node.pos[0] + 10, start_node.pos[1] + 10]\n : LiteGraph.vueNodesMode // TODO: still use LG get pos if vue nodes is off until stable\n ? getSlotPosition(start_node, outputId, false)\n : start_node.getOutputPos(outputId)\n\n const output = start_node.outputs[outputId]\n if (!output) continue\n\n this.#renderAllLinkSegments(\n ctx,\n link,\n startPos,\n endPos,\n visibleReroutes,\n now,\n output.dir,\n input.dir\n )\n }\n }\n\n if (subgraph) {\n for (const output of subgraph.inputNode.slots) {\n if (!output.linkIds.length) continue\n\n // find link info\n for (const linkId of output.linkIds) {\n const resolved = LLink.resolve(linkId, graph)\n if (!resolved) continue\n\n const { link, inputNode, input } = resolved\n if (!inputNode || !input) continue\n\n const endPos = LiteGraph.vueNodesMode\n ? getSlotPosition(inputNode, link.target_slot, true)\n : inputNode.getInputPos(link.target_slot)\n\n this.#renderAllLinkSegments(\n ctx,\n link,\n output.pos,\n endPos,\n visibleReroutes,\n now,\n input.dir,\n input.dir\n )\n }\n }\n\n for (const input of subgraph.outputNode.slots) {\n if (!input.linkIds.length) continue\n\n // find link info\n const resolved = LLink.resolve(input.linkIds[0], graph)\n if (!resolved) continue\n\n const { link, outputNode, output } = resolved\n if (!outputNode || !output) continue\n\n const startPos = LiteGraph.vueNodesMode\n ? getSlotPosition(outputNode, link.origin_slot, false)\n : outputNode.getOutputPos(link.origin_slot)\n\n this.#renderAllLinkSegments(\n ctx,\n link,\n startPos,\n input.pos,\n visibleReroutes,\n now,\n output.dir,\n input.dir\n )\n }\n }\n\n if (graph.floatingLinks.size > 0) {\n this.#renderFloatingLinks(ctx, graph, visibleReroutes, now)\n }\n\n const rerouteSet = this.#visibleReroutes\n rerouteSet.clear()\n\n // Render reroutes, ordered by number of non-floating links\n visibleReroutes.sort((a, b) => a.linkIds.size - b.linkIds.size)\n for (const reroute of visibleReroutes) {\n rerouteSet.add(reroute)\n\n if (\n this.#snapToGrid &&\n this.isDragging &&\n this.selectedItems.has(reroute)\n ) {\n this.drawSnapGuide(ctx, reroute, RenderShape.CIRCLE)\n }\n reroute.draw(ctx, this._pattern)\n\n // Never draw slots when the pointer is down\n if (!this.pointer.isDown) reroute.drawSlots(ctx)\n }\n ctx.globalAlpha = 1\n }\n\n private getNodeModeAlpha(node: LGraphNode) {\n return node.mode === LGraphEventMode.BYPASS\n ? 0.2\n : node.mode === LGraphEventMode.NEVER\n ? 0.4\n : this.editor_alpha\n }\n\n #renderFloatingLinks(\n ctx: CanvasRenderingContext2D,\n graph: LGraph,\n visibleReroutes: Reroute[],\n now: number\n ) {\n // Render floating links with 3/4 current alpha\n const { globalAlpha } = ctx\n ctx.globalAlpha = globalAlpha * 0.33\n\n // Floating reroutes\n for (const link of graph.floatingLinks.values()) {\n const reroutes = LLink.getReroutes(graph, link)\n const firstReroute = reroutes[0]\n const reroute = reroutes.at(-1)\n if (!firstReroute || !reroute?.floating) continue\n\n // Input not connected\n if (reroute.floating.slotType === 'input') {\n const node = graph.getNodeById(link.target_id)\n if (!node) continue\n\n const startPos = firstReroute.pos\n const endPos: Point = LiteGraph.vueNodesMode\n ? getSlotPosition(node, link.target_slot, true)\n : node.getInputPos(link.target_slot)\n const endDirection = node.inputs[link.target_slot]?.dir\n\n firstReroute._dragging = true\n this.#renderAllLinkSegments(\n ctx,\n link,\n startPos,\n endPos,\n visibleReroutes,\n now,\n LinkDirection.CENTER,\n endDirection,\n true\n )\n } else {\n const node = graph.getNodeById(link.origin_id)\n if (!node) continue\n\n const startPos: Point = LiteGraph.vueNodesMode\n ? getSlotPosition(node, link.origin_slot, false)\n : node.getOutputPos(link.origin_slot)\n const endPos = reroute.pos\n const startDirection = node.outputs[link.origin_slot]?.dir\n\n link._dragging = true\n this.#renderAllLinkSegments(\n ctx,\n link,\n startPos,\n endPos,\n visibleReroutes,\n now,\n startDirection,\n LinkDirection.CENTER,\n true\n )\n }\n }\n ctx.globalAlpha = globalAlpha\n }\n\n #renderAllLinkSegments(\n ctx: CanvasRenderingContext2D,\n link: LLink,\n startPos: Point,\n endPos: Point,\n visibleReroutes: Reroute[],\n now: number,\n startDirection?: LinkDirection,\n endDirection?: LinkDirection,\n disabled: boolean = false\n ) {\n const { graph, renderedPaths } = this\n if (!graph) return\n\n // Get all points this link passes through\n const reroutes = LLink.getReroutes(graph, link)\n const points: [Point, ...Point[], Point] = [\n startPos,\n ...reroutes.map((x) => x.pos),\n endPos\n ]\n\n // Bounding box of all points (bezier overshoot on long links will be cut)\n const pointsX = points.map((x) => x[0])\n const pointsY = points.map((x) => x[1])\n link_bounding[0] = Math.min(...pointsX)\n link_bounding[1] = Math.min(...pointsY)\n link_bounding[2] = Math.max(...pointsX) - link_bounding[0]\n link_bounding[3] = Math.max(...pointsY) - link_bounding[1]\n\n // skip links outside of the visible area of the canvas\n if (!overlapBounding(link_bounding, margin_area)) return\n\n const start_dir = startDirection || LinkDirection.RIGHT\n const end_dir = endDirection || LinkDirection.LEFT\n\n // Has reroutes\n if (reroutes.length) {\n let startControl: Point | undefined\n\n const l = reroutes.length\n for (let j = 0; j < l; j++) {\n const reroute = reroutes[j]\n\n // Only render once\n if (!renderedPaths.has(reroute)) {\n renderedPaths.add(reroute)\n visibleReroutes.push(reroute)\n reroute._colour =\n link.color ||\n LGraphCanvas.link_type_colors[link.type] ||\n this.default_link_color\n\n const prevReroute = graph.getReroute(reroute.parentId)\n const rerouteStartPos = prevReroute?.pos ?? startPos\n reroute.calculateAngle(this.last_draw_time, graph, rerouteStartPos)\n\n // Skip the first segment if it is being dragged\n if (!reroute._dragging) {\n this.renderLink(\n ctx,\n rerouteStartPos,\n reroute.pos,\n link,\n false,\n 0,\n null,\n startControl === undefined ? start_dir : LinkDirection.CENTER,\n LinkDirection.CENTER,\n {\n startControl,\n endControl: reroute.controlPoint,\n reroute,\n disabled\n }\n )\n }\n }\n\n if (!startControl && reroutes.at(-1)?.floating?.slotType === 'input') {\n // Floating link connected to an input\n startControl = [0, 0]\n } else {\n // Calculate start control for the next iter control point\n const nextPos = reroutes[j + 1]?.pos ?? endPos\n const dist = Math.min(\n Reroute.maxSplineOffset,\n distance(reroute.pos, nextPos) * 0.25\n )\n startControl = [dist * reroute.cos, dist * reroute.sin]\n }\n }\n\n // Skip the last segment if it is being dragged\n if (link._dragging) return\n\n // Use runtime fallback; TypeScript cannot evaluate this correctly.\n const segmentStartPos = points.at(-2) ?? startPos\n\n // Render final link segment\n this.renderLink(\n ctx,\n segmentStartPos,\n endPos,\n link,\n false,\n 0,\n null,\n LinkDirection.CENTER,\n end_dir,\n { startControl, disabled }\n )\n // Skip normal render when link is being dragged\n } else if (!link._dragging) {\n this.renderLink(\n ctx,\n startPos,\n endPos,\n link,\n false,\n 0,\n null,\n start_dir,\n end_dir\n )\n }\n renderedPaths.add(link)\n\n // event triggered rendered on top\n if (link?._last_time && now - link._last_time < 1000) {\n const f = 2.0 - (now - link._last_time) * 0.002\n const tmp = ctx.globalAlpha\n ctx.globalAlpha = tmp * f\n this.renderLink(\n ctx,\n startPos,\n endPos,\n link,\n true,\n f,\n 'white',\n start_dir,\n end_dir\n )\n ctx.globalAlpha = tmp\n }\n }\n\n /**\n * Build LinkRenderContext from canvas properties\n * Helper method for using LitegraphLinkAdapter\n */\n private buildLinkRenderContext(): LinkRenderContext {\n return {\n // Canvas settings\n renderMode: this.links_render_mode,\n connectionWidth: this.connections_width,\n renderBorder: this.render_connections_border,\n lowQuality: this.low_quality,\n highQualityRender: this.highquality_render,\n scale: this.ds.scale,\n linkMarkerShape: this.linkMarkerShape,\n renderConnectionArrows: this.render_connection_arrows,\n\n // State\n highlightedLinks: new Set(Object.keys(this.highlighted_links)),\n\n // Colors\n defaultLinkColor: this.default_link_color,\n linkTypeColors: LGraphCanvas.link_type_colors,\n\n // Pattern for disabled links\n disabledPattern: this._pattern\n }\n }\n\n /**\n * draws a link between two points\n * @param ctx Canvas 2D rendering context\n * @param a start pos\n * @param b end pos\n * @param link the link object with all the link info\n * @param skip_border ignore the shadow of the link\n * @param flow show flow animation (for events)\n * @param color the color for the link\n * @param start_dir the direction enum\n * @param end_dir the direction enum\n */\n renderLink(\n ctx: CanvasRenderingContext2D,\n a: Readonly<Point>,\n b: Readonly<Point>,\n link: LLink | null,\n skip_border: boolean,\n flow: number | null,\n color: CanvasColour | null,\n start_dir: LinkDirection,\n end_dir: LinkDirection,\n {\n startControl,\n endControl,\n reroute,\n num_sublines = 1,\n disabled = false\n }: {\n /** When defined, render data will be saved to this reroute instead of the {@link link}. */\n reroute?: Reroute\n /** Offset of the bezier curve control point from {@link a point a} (output side) */\n startControl?: Readonly<Point>\n /** Offset of the bezier curve control point from {@link b point b} (input side) */\n endControl?: Readonly<Point>\n /** Number of sublines (useful to represent vec3 or rgb) @todo If implemented, refactor calculations out of the loop */\n num_sublines?: number\n /** Whether this is a floating link segment */\n disabled?: boolean\n } = {}\n ): void {\n if (this.linkRenderer) {\n const context = this.buildLinkRenderContext()\n this.linkRenderer.renderLinkDirect(\n ctx,\n a,\n b,\n link,\n skip_border,\n flow,\n color,\n start_dir,\n end_dir,\n context,\n {\n reroute,\n startControl,\n endControl,\n num_sublines,\n disabled\n }\n )\n }\n }\n\n drawExecutionOrder(ctx: CanvasRenderingContext2D): void {\n ctx.shadowColor = 'transparent'\n ctx.globalAlpha = 0.25\n\n ctx.textAlign = 'center'\n ctx.strokeStyle = 'white'\n ctx.globalAlpha = 0.75\n\n const { visible_nodes } = this\n for (const node of visible_nodes) {\n ctx.fillStyle = 'black'\n ctx.fillRect(\n node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT,\n node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT,\n LiteGraph.NODE_TITLE_HEIGHT,\n LiteGraph.NODE_TITLE_HEIGHT\n )\n if (node.order == 0) {\n ctx.strokeRect(\n node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5,\n node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5,\n LiteGraph.NODE_TITLE_HEIGHT,\n LiteGraph.NODE_TITLE_HEIGHT\n )\n }\n ctx.fillStyle = '#FFF'\n ctx.fillText(\n toString(node.order),\n node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5,\n node.pos[1] - 6\n )\n }\n ctx.globalAlpha = 1\n }\n\n /**\n * draws the widgets stored inside a node\n * @deprecated Use {@link LGraphNode.drawWidgets} instead.\n * @remarks Currently there are extensions hijacking this function, so we cannot remove it.\n */\n drawNodeWidgets(\n node: LGraphNode,\n _posY: null,\n ctx: CanvasRenderingContext2D\n ): void {\n node.drawWidgets(ctx, {\n lowQuality: this.low_quality,\n editorAlpha: this.getNodeModeAlpha(node)\n })\n }\n\n /**\n * draws every group area in the background\n */\n drawGroups(_canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D): void {\n if (!this.graph) return\n\n const groups = this.graph._groups\n\n ctx.save()\n ctx.globalAlpha = 0.5 * this.editor_alpha\n const drawSnapGuides =\n this.#snapToGrid &&\n (this.isDragging || layoutStore.isDraggingVueNodes.value)\n\n for (const group of groups) {\n // out of the visible area\n if (!overlapBounding(this.visible_area, group._bounding)) {\n continue\n }\n\n // Draw snap shadow\n if (drawSnapGuides && this.selectedItems.has(group))\n this.drawSnapGuide(ctx, group)\n\n group.draw(this, ctx)\n }\n\n ctx.restore()\n }\n\n /**\n * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode\n * @todo Remove or rewrite\n */\n resize(width?: number, height?: number): void {\n if (!width && !height) {\n const parent = this.canvas.parentElement\n if (!parent)\n throw new TypeError(\n 'Attempted to resize canvas, but parent element was null.'\n )\n width = parent.offsetWidth\n height = parent.offsetHeight\n }\n\n if (this.canvas.width == width && this.canvas.height == height) return\n\n this.canvas.width = width ?? 0\n this.canvas.height = height ?? 0\n this.bgcanvas.width = this.canvas.width\n this.bgcanvas.height = this.canvas.height\n this.setDirty(true, true)\n }\n\n onNodeSelectionChange(): void {}\n\n /**\n * Determines the furthest nodes in each direction for the currently selected nodes\n */\n boundaryNodesForSelection(): NullableProperties<IBoundaryNodes> {\n return LGraphCanvas.getBoundaryNodes(this.selected_nodes)\n }\n\n showLinkMenu(segment: LinkSegment, e: CanvasPointerEvent): boolean {\n const { graph } = this\n if (!graph) throw new NullGraphError()\n\n const title =\n 'data' in segment && segment.data != null\n ? segment.data.constructor.name\n : undefined\n\n const { origin_id, origin_slot } = segment\n if (origin_id == null || origin_slot == null) {\n new LiteGraph.ContextMenu<string>(['Link has no origin'], {\n event: e,\n title\n })\n return false\n }\n\n const node_left = graph.getNodeById(origin_id)\n const fromType = node_left?.outputs?.[origin_slot]?.type\n\n const options = ['Add Node', 'Add Reroute', null, 'Delete', null]\n\n const menu = new LiteGraph.ContextMenu<string>(options, {\n event: e,\n title,\n callback: inner_clicked.bind(this)\n })\n\n return false\n\n function inner_clicked(\n this: LGraphCanvas,\n v: string,\n _options: unknown,\n e: MouseEvent\n ) {\n if (!graph) throw new NullGraphError()\n\n switch (v) {\n case 'Add Node':\n LGraphCanvas.onMenuAdd(null, null, e, menu, (node) => {\n if (\n !node?.inputs?.length ||\n !node?.outputs?.length ||\n origin_slot == null\n )\n return\n\n // leave the connection type checking inside connectByType\n const options = { afterRerouteId: segment.parentId }\n if (\n node_left?.connectByType(\n origin_slot,\n node,\n fromType ?? '*',\n options\n )\n ) {\n node.pos[0] -= node.size[0] * 0.5\n }\n })\n break\n\n case 'Add Reroute': {\n try {\n this.emitBeforeChange()\n this.adjustMouseEvent(e)\n graph.createReroute(segment._pos, segment)\n this.setDirty(false, true)\n } catch (error) {\n console.error(error)\n } finally {\n this.emitAfterChange()\n }\n break\n }\n\n case 'Delete': {\n // segment can be a Reroute object, in which case segment.id is the reroute id\n const linkId =\n segment instanceof Reroute\n ? segment.linkIds.values().next().value\n : segment.id\n if (linkId !== undefined) {\n graph.removeLink(linkId)\n // Clean up layout store\n layoutStore.deleteLinkLayout(linkId)\n }\n break\n }\n default:\n }\n }\n }\n\n createDefaultNodeForSlot(optPass: ICreateDefaultNodeOptions): boolean {\n type DefaultOptions = ICreateDefaultNodeOptions & {\n posAdd: Point\n posSizeFix: Point\n }\n\n const opts = Object.assign<DefaultOptions, ICreateDefaultNodeOptions>(\n {\n nodeFrom: null,\n slotFrom: null,\n nodeTo: null,\n slotTo: null,\n position: [0, 0],\n nodeType: undefined,\n posAdd: [0, 0],\n posSizeFix: [0, 0]\n },\n optPass\n )\n const { afterRerouteId } = opts\n\n const isFrom = opts.nodeFrom && opts.slotFrom !== null\n const isTo = !isFrom && opts.nodeTo && opts.slotTo !== null\n\n if (!isFrom && !isTo) {\n console.warn(\n `No data passed to createDefaultNodeForSlot`,\n opts.nodeFrom,\n opts.slotFrom,\n opts.nodeTo,\n opts.slotTo\n )\n return false\n }\n if (!opts.nodeType) {\n console.warn('No type to createDefaultNodeForSlot')\n return false\n }\n\n const nodeX = isFrom ? opts.nodeFrom : opts.nodeTo\n if (!nodeX)\n throw new TypeError('nodeX was null when creating default node for slot.')\n\n let slotX = isFrom ? opts.slotFrom : opts.slotTo\n\n let iSlotConn: number | false = false\n if (nodeX instanceof SubgraphIONodeBase) {\n if (typeof slotX !== 'object' || !slotX) {\n console.warn('Cant get slot information', slotX)\n return false\n }\n const { name } = slotX\n iSlotConn = nodeX.slots.findIndex((s) => s.name === name)\n slotX = nodeX.slots[iSlotConn]\n if (!slotX) {\n console.warn('Cant get slot information', slotX)\n return false\n }\n } else {\n switch (typeof slotX) {\n case 'string':\n iSlotConn = isFrom\n ? nodeX.findOutputSlot(slotX, false)\n : nodeX.findInputSlot(slotX, false)\n slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]\n break\n case 'object':\n if (slotX === null) {\n console.warn('Cant get slot information', slotX)\n return false\n }\n\n // ok slotX\n iSlotConn = isFrom\n ? nodeX.findOutputSlot(slotX.name)\n : nodeX.findInputSlot(slotX.name)\n break\n case 'number':\n iSlotConn = slotX\n slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]\n break\n case 'undefined':\n default:\n console.warn('Cant get slot information', slotX)\n return false\n }\n }\n\n // check for defaults nodes for this slottype\n const fromSlotType = slotX.type == LiteGraph.EVENT ? '_event_' : slotX.type\n const slotTypesDefault = isFrom\n ? LiteGraph.slot_types_default_out\n : LiteGraph.slot_types_default_in\n if (slotTypesDefault?.[fromSlotType]) {\n let nodeNewType: string | Record<string, unknown> | false = false\n if (typeof slotTypesDefault[fromSlotType] == 'object') {\n for (const typeX in slotTypesDefault[fromSlotType]) {\n if (\n opts.nodeType == slotTypesDefault[fromSlotType][typeX] ||\n opts.nodeType == 'AUTO'\n ) {\n nodeNewType = slotTypesDefault[fromSlotType][typeX]\n break\n }\n }\n } else if (\n opts.nodeType == slotTypesDefault[fromSlotType] ||\n opts.nodeType == 'AUTO'\n ) {\n nodeNewType = slotTypesDefault[fromSlotType]\n }\n if (nodeNewType) {\n let nodeNewOpts: SlotTypeDefaultNodeOpts | undefined\n let nodeTypeStr: string\n if (typeof nodeNewType == 'object') {\n nodeNewOpts = nodeNewType as SlotTypeDefaultNodeOpts\n nodeTypeStr = nodeNewOpts.node ?? ''\n } else {\n nodeTypeStr = nodeNewType\n }\n\n // that.graph.beforeChange();\n const xSizeFix = opts.posSizeFix[0] * LiteGraph.NODE_WIDTH\n const ySizeFix = opts.posSizeFix[1] * LiteGraph.NODE_SLOT_HEIGHT\n const nodeX = opts.position[0] + opts.posAdd[0] + xSizeFix\n const nodeY = opts.position[1] + opts.posAdd[1] + ySizeFix\n const pos = [nodeX, nodeY]\n const newNode = LiteGraph.createNode(nodeTypeStr, nodeNewOpts?.title, {\n pos\n })\n if (newNode) {\n // if is object pass options\n if (nodeNewOpts) {\n if (nodeNewOpts.properties) {\n for (const i in nodeNewOpts.properties) {\n newNode.addProperty(i, nodeNewOpts.properties[i])\n }\n }\n if (nodeNewOpts.inputs) {\n newNode.inputs = []\n for (const input of nodeNewOpts.inputs) {\n newNode.addInput(input[0], input[1])\n }\n }\n if (nodeNewOpts.outputs) {\n newNode.outputs = []\n for (const output of nodeNewOpts.outputs) {\n newNode.addOutput(output[0], output[1])\n }\n }\n if (nodeNewOpts.json) {\n newNode.configure(nodeNewOpts.json)\n }\n }\n\n // add the node\n if (!this.graph) throw new NullGraphError()\n\n this.graph.add(newNode)\n\n // Interim API - allow the link connection to be canceled.\n // TODO: https://github.com/Comfy-Org/litegraph.js/issues/946\n const detail = { node: newNode, opts }\n const mayConnectLinks = this.canvas.dispatchEvent(\n new CustomEvent('connect-new-default-node', {\n detail,\n cancelable: true\n })\n )\n if (!mayConnectLinks) return true\n\n // connect the two!\n if (isFrom) {\n if (!opts.nodeFrom)\n throw new TypeError(\n 'createDefaultNodeForSlot - nodeFrom was null'\n )\n opts.nodeFrom.connectByType(iSlotConn, newNode, fromSlotType, {\n afterRerouteId\n })\n } else {\n if (!opts.nodeTo)\n throw new TypeError('createDefaultNodeForSlot - nodeTo was null')\n opts.nodeTo.connectByTypeOutput(iSlotConn, newNode, fromSlotType, {\n afterRerouteId\n })\n }\n\n // if connecting in between\n if (isFrom && isTo) {\n // TODO\n }\n\n return true\n }\n console.error(`failed creating ${nodeNewType}`)\n }\n }\n return false\n }\n\n showConnectionMenu(\n optPass: Partial<ICreateNodeOptions & { e: MouseEvent }>\n ): ContextMenu<string> | undefined {\n const opts = Object.assign<\n ICreateNodeOptions & HasShowSearchCallback,\n ICreateNodeOptions\n >(\n {\n nodeFrom: null,\n slotFrom: null,\n nodeTo: null,\n slotTo: null,\n e: undefined,\n allow_searchbox: this.allow_searchbox,\n showSearchBox: this.showSearchBox\n },\n optPass || {}\n )\n const dirty = () => this.#dirty()\n\n const that = this\n const { graph } = this\n const { afterRerouteId } = opts\n\n const isFrom = opts.nodeFrom && opts.slotFrom\n const isTo = !isFrom && opts.nodeTo && opts.slotTo\n\n if (!isFrom && !isTo) {\n console.warn('No data passed to showConnectionMenu')\n return\n }\n\n const nodeX = isFrom ? opts.nodeFrom : opts.nodeTo\n if (!nodeX)\n throw new TypeError('nodeX was null when creating default node for slot.')\n let slotX = isFrom ? opts.slotFrom : opts.slotTo\n\n let iSlotConn: number\n if (nodeX instanceof SubgraphIONodeBase) {\n if (typeof slotX !== 'object' || !slotX) {\n console.warn('Cant get slot information', slotX)\n return\n }\n const { name } = slotX\n iSlotConn = nodeX.slots.findIndex((s) => s.name === name)\n // If it's not found in the main slots, it might be the empty slot from a Subgraph node.\n // In that case, the original `slotX` object is the correct one, so don't overwrite it.\n if (iSlotConn !== -1) {\n slotX = nodeX.slots[iSlotConn]\n }\n if (!slotX) {\n console.warn('Cant get slot information', slotX)\n return\n }\n } else {\n switch (typeof slotX) {\n case 'string':\n iSlotConn = isFrom\n ? nodeX.findOutputSlot(slotX, false)\n : nodeX.findInputSlot(slotX, false)\n slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]\n break\n case 'object':\n if (slotX === null) {\n console.warn('Cant get slot information', slotX)\n return\n }\n\n // ok slotX\n iSlotConn = isFrom\n ? nodeX.findOutputSlot(slotX.name)\n : nodeX.findInputSlot(slotX.name)\n break\n case 'number':\n iSlotConn = slotX\n slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]\n break\n default:\n console.warn('Cant get slot information', slotX)\n return\n }\n }\n\n const options = ['Add Node', 'Add Reroute', null]\n\n if (opts.allow_searchbox) {\n options.push('Search', null)\n }\n\n // get defaults nodes for this slottype\n const fromSlotType = slotX.type == LiteGraph.EVENT ? '_event_' : slotX.type\n const slotTypesDefault = isFrom\n ? LiteGraph.slot_types_default_out\n : LiteGraph.slot_types_default_in\n if (slotTypesDefault?.[fromSlotType]) {\n if (typeof slotTypesDefault[fromSlotType] == 'object') {\n for (const typeX in slotTypesDefault[fromSlotType]) {\n options.push(slotTypesDefault[fromSlotType][typeX])\n }\n } else {\n options.push(slotTypesDefault[fromSlotType])\n }\n }\n\n // build menu\n const menu = new LiteGraph.ContextMenu<string>(options, {\n event: opts.e,\n extra: slotX,\n title:\n (slotX && slotX.name != ''\n ? slotX.name + (fromSlotType ? ' | ' : '')\n : '') + (slotX && fromSlotType ? fromSlotType : ''),\n callback: inner_clicked\n })\n\n return menu\n\n // callback\n function inner_clicked(\n v: string | undefined,\n options: IContextMenuOptions<string, INodeInputSlot | INodeOutputSlot>,\n e: MouseEvent\n ) {\n switch (v) {\n case 'Add Node':\n LGraphCanvas.onMenuAdd(null, null, e, menu, function (node) {\n if (!node) return\n\n if (isFrom) {\n if (!opts.nodeFrom)\n throw new TypeError(\n 'Cannot add node to SubgraphInputNode: nodeFrom was null'\n )\n const slot = opts.nodeFrom.connectByType(\n iSlotConn,\n node,\n fromSlotType,\n { afterRerouteId }\n )\n if (!slot) console.warn('Failed to make new connection.')\n // }\n } else {\n if (!opts.nodeTo)\n throw new TypeError(\n 'Cannot add node to SubgraphInputNode: nodeTo was null'\n )\n opts.nodeTo.connectByTypeOutput(iSlotConn, node, fromSlotType, {\n afterRerouteId\n })\n }\n })\n break\n case 'Add Reroute': {\n const node = isFrom ? opts.nodeFrom : opts.nodeTo\n const slot = options.extra\n\n if (!graph) throw new NullGraphError()\n if (!node) throw new TypeError('Cannot add reroute: node was null')\n if (!slot) throw new TypeError('Cannot add reroute: slot was null')\n if (!opts.e)\n throw new TypeError(\n 'Cannot add reroute: CanvasPointerEvent was null'\n )\n\n if (node instanceof SubgraphIONodeBase) {\n throw new TypeError(\n 'Cannot add floating reroute to Subgraph IO Nodes'\n )\n } else {\n const reroute = node.connectFloatingReroute(\n [opts.e.canvasX, opts.e.canvasY],\n slot,\n afterRerouteId\n )\n if (!reroute) throw new Error('Failed to create reroute')\n }\n\n dirty()\n break\n }\n case 'Search':\n if (isFrom) {\n opts.showSearchBox(e, {\n // @ts-expect-error - Subgraph types\n node_from: opts.nodeFrom,\n // @ts-expect-error - Subgraph types\n slot_from: slotX,\n type_filter_in: fromSlotType\n })\n } else {\n opts.showSearchBox(e, {\n // @ts-expect-error - Subgraph types\n node_to: opts.nodeTo,\n // @ts-expect-error - Subgraph types\n slot_from: slotX,\n type_filter_out: fromSlotType\n })\n }\n break\n default: {\n const customProps = {\n position: [opts.e?.canvasX ?? 0, opts.e?.canvasY ?? 0],\n nodeType: v,\n afterRerouteId\n } satisfies Partial<ICreateDefaultNodeOptions>\n\n const options = Object.assign(opts, customProps)\n if (!that.createDefaultNodeForSlot(options)) break\n }\n }\n }\n }\n\n // refactor: there are different dialogs, some uses createDialog some dont\n prompt(\n title: string,\n value: string | number,\n callback: (value: string) => void,\n event: CanvasPointerEvent,\n multiline?: boolean\n ): HTMLDivElement {\n const that = this\n title = title || ''\n\n const customProperties = {\n is_modified: false,\n className: 'graphdialog rounded',\n innerHTML: multiline\n ? \"<span class='name'></span> <textarea autofocus class='value'></textarea><button class='rounded'>OK</button>\"\n : \"<span class='name'></span> <input autofocus type='text' class='value'/><button class='rounded'>OK</button>\",\n close() {\n that.prompt_box = null\n if (dialog.parentNode) {\n dialog.remove()\n }\n }\n } satisfies Partial<IDialog>\n\n const div = document.createElement('div')\n const dialog: PromptDialog = Object.assign(div, customProperties)\n\n const graphcanvas = LGraphCanvas.active_canvas\n const { canvas } = graphcanvas\n if (!canvas.parentNode)\n throw new TypeError(\n 'canvas element parentNode was null when opening a prompt.'\n )\n canvas.parentNode.append(dialog)\n\n if (this.ds.scale > 1) dialog.style.transform = `scale(${this.ds.scale})`\n\n let dialogCloseTimer: ReturnType<typeof setTimeout> | undefined\n let prevent_timeout = 0\n LiteGraph.pointerListenerAdd(dialog, 'leave', function () {\n if (prevent_timeout) return\n if (LiteGraph.dialog_close_on_mouse_leave) {\n if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) {\n dialogCloseTimer = setTimeout(\n dialog.close,\n LiteGraph.dialog_close_on_mouse_leave_delay\n )\n }\n }\n })\n LiteGraph.pointerListenerAdd(dialog, 'enter', function () {\n if (LiteGraph.dialog_close_on_mouse_leave && dialogCloseTimer)\n clearTimeout(dialogCloseTimer)\n })\n const selInDia = dialog.querySelectorAll('select')\n if (selInDia) {\n // if filtering, check focus changed to comboboxes and prevent closing\n for (const selIn of selInDia) {\n selIn.addEventListener('click', function () {\n prevent_timeout++\n })\n selIn.addEventListener('blur', function () {\n prevent_timeout = 0\n })\n selIn.addEventListener('change', function () {\n prevent_timeout = -1\n })\n }\n }\n this.prompt_box?.close()\n this.prompt_box = dialog\n\n const name_element: HTMLSpanElement | null = dialog.querySelector('.name')\n if (!name_element) throw new TypeError('name_element was null')\n\n name_element.textContent = title\n const value_element: HTMLInputElement | null =\n dialog.querySelector('.value')\n if (!value_element) throw new TypeError('value_element was null')\n\n value_element.value = String(value)\n value_element.select()\n\n const input = value_element\n input.addEventListener('keydown', function (e: KeyboardEvent) {\n dialog.is_modified = true\n if (e.key == 'Escape') {\n // ESC\n dialog.close()\n } else if (\n e.key == 'Enter' &&\n (e.target as Element).localName != 'textarea'\n ) {\n if (callback) {\n callback(this.value)\n }\n dialog.close()\n } else {\n return\n }\n e.preventDefault()\n e.stopPropagation()\n })\n\n const button = dialog.querySelector('button')\n if (!button) throw new TypeError('button was null when opening prompt')\n\n button.addEventListener('click', function () {\n callback?.(input.value)\n that.setDirty(true)\n dialog.close()\n })\n\n const rect = canvas.getBoundingClientRect()\n let offsetx = -20\n let offsety = -20\n if (rect) {\n offsetx -= rect.left\n offsety -= rect.top\n }\n\n if (event) {\n dialog.style.left = `${event.clientX + offsetx}px`\n dialog.style.top = `${event.clientY + offsety}px`\n } else {\n dialog.style.left = `${canvas.width * 0.5 + offsetx}px`\n dialog.style.top = `${canvas.height * 0.5 + offsety}px`\n }\n\n setTimeout(function () {\n input.focus()\n const clickTime = Date.now()\n function handleOutsideClick(e: Event) {\n if (e.target === canvas && Date.now() - clickTime > 256) {\n dialog.close()\n canvas.parentElement?.removeEventListener('click', handleOutsideClick)\n canvas.parentElement?.removeEventListener(\n 'touchend',\n handleOutsideClick\n )\n }\n }\n canvas.parentElement?.addEventListener('click', handleOutsideClick)\n canvas.parentElement?.addEventListener('touchend', handleOutsideClick)\n }, 10)\n\n return dialog\n }\n\n showSearchBox(\n event: MouseEvent | null,\n searchOptions?: IShowSearchOptions\n ): HTMLDivElement {\n // proposed defaults\n const options: IShowSearchOptions = {\n slot_from: null,\n node_from: null,\n node_to: null,\n // TODO check for registered_slot_[in/out]_types not empty\n // this will be checked for functionality enabled : filter on slot type, in and out\n do_type_filter: LiteGraph.search_filter_enabled,\n\n // these are default: pass to set initially set values\n // @ts-expect-error Property missing from interface definition\n type_filter_in: false,\n\n type_filter_out: false,\n show_general_if_none_on_typefilter: true,\n show_general_after_typefiltered: true,\n hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave,\n show_all_if_empty: true,\n show_all_on_open: LiteGraph.search_show_all_on_open\n }\n Object.assign(options, searchOptions)\n\n // console.log(options);\n\n const that = this\n const graphcanvas = LGraphCanvas.active_canvas\n const { canvas } = graphcanvas\n const root_document = canvas.ownerDocument || document\n\n const div = document.createElement('div')\n const dialog = Object.assign(div, {\n close(this: typeof div) {\n that.search_box = undefined\n this.blur()\n canvas.focus()\n root_document.body.style.overflow = ''\n\n // important, if canvas loses focus keys won't be captured\n setTimeout(() => canvas.focus(), 20)\n dialog.remove()\n }\n } satisfies Partial<HTMLDivElement> & ICloseable)\n dialog.className = 'litegraph litesearchbox graphdialog rounded'\n dialog.innerHTML =\n \"<span class='name'>Search</span> <input autofocus type='text' class='value rounded'/>\"\n if (options.do_type_filter) {\n dialog.innerHTML +=\n \"<select class='slot_in_type_filter'><option value=''></option></select>\"\n dialog.innerHTML +=\n \"<select class='slot_out_type_filter'><option value=''></option></select>\"\n }\n const helper = document.createElement('div')\n helper.className = 'helper'\n dialog.append(helper)\n\n if (root_document.fullscreenElement) {\n root_document.fullscreenElement.append(dialog)\n } else {\n root_document.body.append(dialog)\n root_document.body.style.overflow = 'hidden'\n }\n\n // dialog element has been appended\n let selIn\n let selOut\n if (options.do_type_filter) {\n selIn = dialog.querySelector('.slot_in_type_filter')\n selOut = dialog.querySelector('.slot_out_type_filter')\n }\n\n if (this.ds.scale > 1) {\n dialog.style.transform = `scale(${this.ds.scale})`\n }\n\n // hide on mouse leave\n if (options.hide_on_mouse_leave) {\n let prevent_timeout = 0\n let timeout_close: ReturnType<typeof setTimeout> | null = null\n LiteGraph.pointerListenerAdd(dialog, 'enter', function () {\n if (timeout_close) {\n clearTimeout(timeout_close)\n timeout_close = null\n }\n })\n dialog.addEventListener('pointerleave', function () {\n if (prevent_timeout) return\n\n const hideDelay = options.hide_on_mouse_leave\n const delay = typeof hideDelay === 'number' ? hideDelay : 500\n timeout_close = setTimeout(dialog.close, delay)\n })\n // if filtering, check focus changed to comboboxes and prevent closing\n if (options.do_type_filter) {\n if (!selIn)\n throw new TypeError('selIn was null when showing search box')\n if (!selOut)\n throw new TypeError('selOut was null when showing search box')\n\n selIn.addEventListener('click', function () {\n prevent_timeout++\n })\n selIn.addEventListener('blur', function () {\n prevent_timeout = 0\n })\n selIn.addEventListener('change', function () {\n prevent_timeout = -1\n })\n selOut.addEventListener('click', function () {\n prevent_timeout++\n })\n selOut.addEventListener('blur', function () {\n prevent_timeout = 0\n })\n selOut.addEventListener('change', function () {\n prevent_timeout = -1\n })\n }\n }\n\n // @ts-expect-error Panel?\n that.search_box?.close()\n that.search_box = dialog\n\n let first: string | null = null\n let timeout: ReturnType<typeof setTimeout> | null = null\n let selected: ChildNode | null = null\n\n const maybeInput = dialog.querySelector('input')\n if (!maybeInput) throw new TypeError('Could not create search input box.')\n\n const input = maybeInput\n\n if (input) {\n input.addEventListener('blur', function () {\n this.focus()\n })\n input.addEventListener('keydown', function (e) {\n if (e.key == 'ArrowUp') {\n // UP\n changeSelection(false)\n } else if (e.key == 'ArrowDown') {\n // DOWN\n changeSelection(true)\n } else if (e.key == 'Escape') {\n // ESC\n dialog.close()\n } else if (e.key == 'Enter') {\n if (selected instanceof HTMLElement) {\n select(unescape(String(selected.dataset['type'])))\n } else if (first) {\n select(first)\n } else {\n dialog.close()\n }\n } else {\n if (timeout) {\n clearInterval(timeout)\n }\n timeout = setTimeout(refreshHelper, 10)\n return\n }\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n return true\n })\n }\n\n // if should filter on type, load and fill selected and choose elements if passed\n if (options.do_type_filter) {\n if (selIn) {\n const aSlots = LiteGraph.slot_types_in\n const nSlots = aSlots.length\n\n if (\n options.type_filter_in == LiteGraph.EVENT ||\n options.type_filter_in == LiteGraph.ACTION\n ) {\n options.type_filter_in = '_event_'\n }\n for (let iK = 0; iK < nSlots; iK++) {\n const opt = document.createElement('option')\n opt.value = aSlots[iK]\n opt.innerHTML = aSlots[iK]\n selIn.append(opt)\n if (\n // @ts-expect-error Property missing from interface definition\n options.type_filter_in !== false &&\n String(options.type_filter_in).toLowerCase() ==\n String(aSlots[iK]).toLowerCase()\n ) {\n opt.selected = true\n }\n }\n selIn.addEventListener('change', function () {\n refreshHelper()\n })\n }\n if (selOut) {\n const aSlots = LiteGraph.slot_types_out\n\n if (\n options.type_filter_out == LiteGraph.EVENT ||\n options.type_filter_out == LiteGraph.ACTION\n ) {\n options.type_filter_out = '_event_'\n }\n for (const aSlot of aSlots) {\n const opt = document.createElement('option')\n opt.value = aSlot\n opt.innerHTML = aSlot\n selOut.append(opt)\n if (\n options.type_filter_out !== false &&\n String(options.type_filter_out).toLowerCase() ==\n String(aSlot).toLowerCase()\n ) {\n opt.selected = true\n }\n }\n selOut.addEventListener('change', function () {\n refreshHelper()\n })\n }\n }\n\n // compute best position\n const rect = canvas.getBoundingClientRect()\n\n // Handles cases where the searchbox is initiated by\n // non-click events. e.g. Keyboard shortcuts\n const safeEvent =\n event ??\n new MouseEvent('click', {\n clientX: rect.left + rect.width * 0.5,\n clientY: rect.top + rect.height * 0.5,\n // @ts-expect-error layerY is a nonstandard property\n layerY: rect.top + rect.height * 0.5\n })\n\n const left = safeEvent.clientX - 80\n const top = safeEvent.clientY - 20\n dialog.style.left = `${left}px`\n dialog.style.top = `${top}px`\n\n // To avoid out of screen problems\n if (safeEvent.layerY > rect.height - 200) {\n helper.style.maxHeight = `${rect.height - safeEvent.layerY - 20}px`\n }\n requestAnimationFrame(function () {\n input.focus()\n })\n if (options.show_all_on_open) refreshHelper()\n\n function select(name: string) {\n if (name) {\n if (that.onSearchBoxSelection) {\n that.onSearchBoxSelection(name, safeEvent, graphcanvas)\n } else {\n if (!graphcanvas.graph) throw new NullGraphError()\n\n graphcanvas.graph.beforeChange()\n const node = LiteGraph.createNode(name)\n if (node) {\n node.pos = graphcanvas.convertEventToCanvasOffset(safeEvent)\n graphcanvas.graph.add(node, false)\n }\n\n // join node after inserting\n if (options.node_from) {\n let iS: number | false = false\n switch (typeof options.slot_from) {\n case 'string':\n iS = options.node_from.findOutputSlot(options.slot_from)\n break\n case 'object':\n if (options.slot_from == null)\n throw new TypeError(\n 'options.slot_from was null when showing search box'\n )\n\n iS = options.slot_from.name\n ? options.node_from.findOutputSlot(options.slot_from.name)\n : -1\n // @ts-expect-error - slot_index property\n if (iS == -1 && options.slot_from.slot_index !== undefined)\n // @ts-expect-error - slot_index property\n iS = options.slot_from.slot_index\n break\n case 'number':\n iS = options.slot_from\n break\n default:\n // try with first if no name set\n iS = 0\n }\n if (iS !== false && options.node_from.outputs[iS] !== undefined) {\n if (iS > -1) {\n if (node == null)\n throw new TypeError(\n 'options.slot_from was null when showing search box'\n )\n\n options.node_from.connectByType(\n iS,\n node,\n options.node_from.outputs[iS].type\n )\n }\n } else {\n // console.warn(\"can't find slot \" + options.slot_from);\n }\n }\n if (options.node_to) {\n let iS: number | false = false\n switch (typeof options.slot_from) {\n case 'string':\n iS = options.node_to.findInputSlot(options.slot_from)\n break\n case 'object':\n if (options.slot_from == null)\n throw new TypeError(\n 'options.slot_from was null when showing search box'\n )\n\n iS = options.slot_from.name\n ? options.node_to.findInputSlot(options.slot_from.name)\n : -1\n // @ts-expect-error - slot_index property\n if (iS == -1 && options.slot_from.slot_index !== undefined)\n // @ts-expect-error - slot_index property\n iS = options.slot_from.slot_index\n break\n case 'number':\n iS = options.slot_from\n break\n default:\n // try with first if no name set\n iS = 0\n }\n if (iS !== false && options.node_to.inputs[iS] !== undefined) {\n if (iS > -1) {\n if (node == null)\n throw new TypeError(\n 'options.slot_from was null when showing search box'\n )\n // try connection\n options.node_to.connectByTypeOutput(\n iS,\n node,\n options.node_to.inputs[iS].type\n )\n }\n } else {\n // console.warn(\"can't find slot_nodeTO \" + options.slot_from);\n }\n }\n\n graphcanvas.graph.afterChange()\n }\n }\n\n dialog.close()\n }\n\n function changeSelection(forward: boolean) {\n const prev = selected\n if (!selected) {\n selected = forward\n ? helper.childNodes[0]\n : helper.childNodes[helper.childNodes.length]\n } else if (selected instanceof Element) {\n selected.classList.remove('selected')\n selected = forward ? selected.nextSibling : selected.previousSibling\n selected ||= prev\n }\n\n if (selected instanceof Element) {\n selected.classList.add('selected')\n selected.scrollIntoView({ block: 'end', behavior: 'smooth' })\n }\n }\n\n function refreshHelper() {\n timeout = null\n let str = input.value\n first = null\n helper.innerHTML = ''\n if (!str && !options.show_all_if_empty) return\n\n if (that.onSearchBox) {\n const list = that.onSearchBox(helper, str, graphcanvas)\n if (list) {\n for (const item of list) {\n addResult(item)\n }\n }\n } else {\n let c = 0\n str = str.toLowerCase()\n if (!graphcanvas.graph) throw new NullGraphError()\n\n const filter = graphcanvas.filter || graphcanvas.graph.filter\n\n // filter by type preprocess\n let sIn: HTMLSelectElement | null = null\n let sOut: HTMLSelectElement | null = null\n if (options.do_type_filter && that.search_box) {\n sIn = that.search_box.querySelector<HTMLSelectElement>(\n '.slot_in_type_filter'\n )\n sOut = that.search_box.querySelector<HTMLSelectElement>(\n '.slot_out_type_filter'\n )\n }\n\n const keys = Object.keys(LiteGraph.registered_node_types)\n const filtered = keys.filter((x) => inner_test_filter(x))\n\n for (const item of filtered) {\n addResult(item)\n if (\n LGraphCanvas.search_limit !== -1 &&\n c++ > LGraphCanvas.search_limit\n )\n break\n }\n\n // add general type if filtering\n if (\n options.show_general_after_typefiltered &&\n (sIn?.value || sOut?.value)\n ) {\n const filtered_extra: string[] = []\n for (const i in LiteGraph.registered_node_types) {\n if (\n inner_test_filter(i, {\n inTypeOverride: sIn && sIn.value ? '*' : false,\n outTypeOverride: sOut && sOut.value ? '*' : false\n })\n ) {\n filtered_extra.push(i)\n }\n }\n for (const extraItem of filtered_extra) {\n addResult(extraItem, 'generic_type')\n if (\n LGraphCanvas.search_limit !== -1 &&\n c++ > LGraphCanvas.search_limit\n )\n break\n }\n }\n\n // check il filtering gave no results\n if (\n (sIn?.value || sOut?.value) &&\n helper.childNodes.length == 0 &&\n options.show_general_if_none_on_typefilter\n ) {\n const filtered_extra: string[] = []\n for (const i in LiteGraph.registered_node_types) {\n if (inner_test_filter(i, { skipFilter: true }))\n filtered_extra.push(i)\n }\n for (const extraItem of filtered_extra) {\n addResult(extraItem, 'not_in_filter')\n if (\n LGraphCanvas.search_limit !== -1 &&\n c++ > LGraphCanvas.search_limit\n )\n break\n }\n }\n\n function inner_test_filter(\n type: string,\n optsIn?: {\n inTypeOverride?: string | boolean\n outTypeOverride?: string | boolean\n skipFilter?: boolean\n }\n ): boolean {\n optsIn = optsIn || {}\n const optsDef = {\n skipFilter: false,\n inTypeOverride: false,\n outTypeOverride: false\n }\n const opts = Object.assign(optsDef, optsIn)\n const ctor = LiteGraph.registered_node_types[type]\n if (filter && ctor.filter != filter) return false\n if (\n (!options.show_all_if_empty || str) &&\n !type.toLowerCase().includes(str) &&\n (!ctor.title || !ctor.title.toLowerCase().includes(str))\n ) {\n return false\n }\n\n // filter by slot IN, OUT types\n if (options.do_type_filter && !opts.skipFilter) {\n const sType = type\n\n let sV: string | undefined =\n typeof opts.inTypeOverride === 'string'\n ? opts.inTypeOverride\n : sIn?.value\n // type is stored\n if (sIn && sV && LiteGraph.registered_slot_in_types[sV]?.nodes) {\n const doesInc =\n LiteGraph.registered_slot_in_types[sV].nodes.includes(sType)\n if (doesInc === false) return false\n }\n\n sV = sOut?.value\n if (typeof opts.outTypeOverride === 'string')\n sV = opts.outTypeOverride\n // type is stored\n if (sOut && sV && LiteGraph.registered_slot_out_types[sV]?.nodes) {\n const doesInc =\n LiteGraph.registered_slot_out_types[sV].nodes.includes(sType)\n if (doesInc === false) return false\n }\n }\n return true\n }\n }\n\n function addResult(type: string, className?: string): void {\n const help = document.createElement('div')\n first ||= type\n\n const nodeType = LiteGraph.registered_node_types[type]\n if (nodeType?.title) {\n help.textContent = nodeType?.title\n const typeEl = document.createElement('span')\n typeEl.className = 'litegraph lite-search-item-type'\n typeEl.textContent = type\n help.append(typeEl)\n } else {\n help.textContent = type\n }\n\n help.dataset['type'] = escape(type)\n help.className = 'litegraph lite-search-item'\n if (className) {\n help.className += ` ${className}`\n }\n help.addEventListener('click', function () {\n select(unescape(String(this.dataset['type'])))\n })\n helper.append(help)\n }\n }\n\n return dialog\n }\n\n showEditPropertyValue(\n node: LGraphNode,\n property: string,\n options: IDialogOptions\n ): IDialog | undefined {\n if (!node || node.properties[property] === undefined) return\n\n options = options || {}\n\n const info = node.getPropertyInfo(property)\n const { type } = info\n\n let input_html = ''\n\n if (\n type == 'string' ||\n type == 'number' ||\n type == 'array' ||\n type == 'object'\n ) {\n input_html = \"<input autofocus type='text' class='value'/>\"\n } else if ((type == 'enum' || type == 'combo') && info.values) {\n input_html = \"<select autofocus type='text' class='value'>\"\n for (const i in info.values) {\n const v = Array.isArray(info.values) ? info.values[i] : i\n\n const selected = v == node.properties[property] ? 'selected' : ''\n input_html += `<option value='${v}' ${selected}>${info.values[i]}</option>`\n }\n input_html += '</select>'\n } else if (type == 'boolean' || type == 'toggle') {\n const checked = node.properties[property] ? 'checked' : ''\n input_html = `<input autofocus type='checkbox' class='value' ${checked}/>`\n } else {\n console.warn(`unknown type: ${type}`)\n return\n }\n\n const dialog = this.createDialog(\n `<span class='name'>${info.label || property}</span>${input_html}<button>OK</button>`,\n options\n )\n\n let input: HTMLInputElement | HTMLSelectElement | null\n if ((type == 'enum' || type == 'combo') && info.values) {\n input = dialog.querySelector('select')\n input?.addEventListener('change', function (e) {\n dialog.modified()\n setValue((e.target as HTMLSelectElement)?.value)\n })\n } else if (type == 'boolean' || type == 'toggle') {\n input = dialog.querySelector('input')\n input?.addEventListener('click', function () {\n dialog.modified()\n // @ts-expect-error setValue function signature not strictly typed\n setValue(!!input.checked)\n })\n } else {\n input = dialog.querySelector('input')\n if (input) {\n input.addEventListener('blur', function () {\n this.focus()\n })\n\n let v =\n node.properties[property] !== undefined\n ? node.properties[property]\n : ''\n if (type !== 'string') {\n v = JSON.stringify(v)\n }\n\n // @ts-expect-error HTMLInputElement.value expects string but v can be other types\n input.value = v\n input.addEventListener('keydown', function (e) {\n if (e.key == 'Escape') {\n // ESC\n dialog.close()\n } else if (e.key == 'Enter') {\n // ENTER\n // save\n inner()\n } else {\n dialog.modified()\n return\n }\n e.preventDefault()\n e.stopPropagation()\n })\n }\n }\n input?.focus()\n\n const button = dialog.querySelector('button')\n if (!button)\n throw new TypeError('Show edit property value button was null.')\n button.addEventListener('click', inner)\n\n function inner() {\n setValue(input?.value)\n }\n const dirty = () => this.#dirty()\n\n function setValue(value: string | number | undefined) {\n if (\n info?.values &&\n typeof info.values === 'object' &&\n info.values[value] != undefined\n ) {\n value = info.values[value]\n }\n\n if (typeof node.properties[property] == 'number') {\n value = Number(value)\n }\n if (type == 'array' || type == 'object') {\n // @ts-expect-error JSON.parse doesn't care.\n value = JSON.parse(value)\n }\n node.properties[property] = value\n if (node.graph) {\n node.graph._version++\n }\n node.onPropertyChanged?.(property, value)\n options.onclose?.()\n dialog.close()\n dirty()\n }\n\n return dialog\n }\n\n // TODO refactor, there are different dialog, some uses createDialog, some dont\n createDialog(html: string, options: IDialogOptions): IDialog {\n const def_options = {\n checkForInput: false,\n closeOnLeave: true,\n closeOnLeave_checkModified: true\n }\n options = Object.assign(def_options, options || {})\n\n const customProperties = {\n className: 'graphdialog',\n innerHTML: html,\n is_modified: false,\n modified() {\n this.is_modified = true\n },\n close(this: IDialog) {\n this.remove()\n }\n } satisfies Partial<IDialog>\n\n const div = document.createElement('div')\n const dialog: IDialog = Object.assign(div, customProperties)\n\n const rect = this.canvas.getBoundingClientRect()\n let offsetx = -20\n let offsety = -20\n if (rect) {\n offsetx -= rect.left\n offsety -= rect.top\n }\n\n if (options.position) {\n offsetx += options.position[0]\n offsety += options.position[1]\n } else if (options.event) {\n offsetx += options.event.clientX\n offsety += options.event.clientY\n } else {\n // centered\n offsetx += this.canvas.width * 0.5\n offsety += this.canvas.height * 0.5\n }\n\n dialog.style.left = `${offsetx}px`\n dialog.style.top = `${offsety}px`\n\n if (!this.canvas.parentNode)\n throw new TypeError('Canvas parent element was null.')\n this.canvas.parentNode.append(dialog)\n\n // acheck for input and use default behaviour: save on enter, close on esc\n if (options.checkForInput) {\n const aI = dialog.querySelectorAll('input')\n if (aI) {\n for (const iX of aI) {\n iX.addEventListener('keydown', function (e) {\n dialog.modified()\n if (e.key == 'Escape') {\n dialog.close()\n } else if (e.key != 'Enter') {\n return\n }\n e.preventDefault()\n e.stopPropagation()\n })\n iX.focus()\n }\n }\n }\n\n let dialogCloseTimer: ReturnType<typeof setTimeout> | undefined\n let prevent_timeout = 0\n dialog.addEventListener('mouseleave', function () {\n if (prevent_timeout) return\n\n if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) {\n dialogCloseTimer = setTimeout(\n dialog.close,\n LiteGraph.dialog_close_on_mouse_leave_delay\n )\n }\n })\n dialog.addEventListener('mouseenter', function () {\n if (options.closeOnLeave || LiteGraph.dialog_close_on_mouse_leave) {\n if (dialogCloseTimer) clearTimeout(dialogCloseTimer)\n }\n })\n const selInDia = dialog.querySelectorAll('select')\n // if filtering, check focus changed to comboboxes and prevent closing\n if (selInDia) {\n for (const selIn of selInDia) {\n selIn.addEventListener('click', function () {\n prevent_timeout++\n })\n selIn.addEventListener('blur', function () {\n prevent_timeout = 0\n })\n selIn.addEventListener('change', function () {\n prevent_timeout = -1\n })\n }\n }\n\n return dialog\n }\n\n createPanel(title: string, options: ICreatePanelOptions): Panel {\n options = options || {}\n\n const root = document.createElement('div') as Panel\n root.className = 'litegraph dialog'\n root.innerHTML =\n \"<div class='dialog-header'><span class='dialog-title'></span></div><div class='dialog-content'></div><div style='display:none;' class='dialog-alt-content'></div><div class='dialog-footer'></div>\"\n root.header = root.querySelector('.dialog-header')!\n\n if (options.width)\n root.style.width =\n options.width + (typeof options.width === 'number' ? 'px' : '')\n if (options.height)\n root.style.height =\n options.height + (typeof options.height === 'number' ? 'px' : '')\n if (options.closable) {\n const close = document.createElement('span')\n close.innerHTML = '&#10005;'\n close.classList.add('close')\n close.addEventListener('click', function () {\n root.close()\n })\n root.header.append(close)\n }\n root.title_element = root.querySelector('.dialog-title')!\n root.title_element.textContent = title\n root.content = root.querySelector('.dialog-content')!\n root.alt_content = root.querySelector('.dialog-alt-content')!\n root.footer = root.querySelector('.dialog-footer')!\n root.footer.style.marginTop = '-96px'\n\n root.close = function () {\n if (typeof root.onClose == 'function') root.onClose()\n root.remove()\n this.remove()\n }\n\n // function to swap panel content\n root.toggleAltContent = function (force?: boolean) {\n let vTo: string\n let vAlt: string\n if (force !== undefined) {\n vTo = force ? 'block' : 'none'\n vAlt = force ? 'none' : 'block'\n } else {\n vTo = root.alt_content.style.display != 'block' ? 'block' : 'none'\n vAlt = root.alt_content.style.display != 'block' ? 'none' : 'block'\n }\n root.alt_content.style.display = vTo\n root.content.style.display = vAlt\n }\n\n root.toggleFooterVisibility = function (force?: boolean) {\n let vTo: string\n if (force !== undefined) {\n vTo = force ? 'block' : 'none'\n } else {\n vTo = root.footer.style.display != 'block' ? 'block' : 'none'\n }\n root.footer.style.display = vTo\n }\n\n root.clear = function () {\n this.content.innerHTML = ''\n }\n\n root.addHTML = function (\n code: string,\n classname?: string,\n on_footer?: boolean\n ) {\n const elem = document.createElement('div')\n if (classname) elem.className = classname\n elem.innerHTML = code\n if (on_footer) root.footer.append(elem)\n else root.content.append(elem)\n return elem\n }\n\n root.addButton = function (\n name: string,\n callback: () => void,\n options?: unknown\n ): PanelButton {\n const elem = document.createElement('button') as PanelButton\n elem.textContent = name\n elem.options = options\n elem.classList.add('btn')\n elem.addEventListener('click', callback)\n root.footer.append(elem)\n return elem\n }\n\n root.addSeparator = function () {\n const elem = document.createElement('div')\n elem.className = 'separator'\n root.content.append(elem)\n }\n\n root.addWidget = function (\n type: string,\n name: string,\n value: TWidgetValue,\n options?: PanelWidgetOptions,\n callback?: PanelWidgetCallback\n ): PanelWidget {\n options = options || {}\n let str_value = String(value)\n type = type.toLowerCase()\n if (type == 'number' && typeof value === 'number')\n str_value = value.toFixed(3)\n\n const elem: PanelWidget = document.createElement('div') as PanelWidget\n elem.className = 'property'\n elem.innerHTML =\n \"<span class='property_name'></span><span class='property_value'></span>\"\n const nameSpan = elem.querySelector('.property_name')\n if (!nameSpan) throw new TypeError('Property name element was null.')\n\n nameSpan.textContent = options.label || name\n const value_element: HTMLSpanElement | null =\n elem.querySelector('.property_value')\n if (!value_element) throw new TypeError('Property name element was null.')\n value_element.textContent = str_value\n elem.dataset['property'] = name\n elem.dataset['type'] = options.type || type\n elem.options = options\n elem.value = value\n\n if (type == 'code') {\n elem.addEventListener('click', function () {\n const property = this.dataset['property']\n if (property) root.inner_showCodePad?.(property)\n })\n } else if (type == 'boolean') {\n elem.classList.add('boolean')\n if (value) elem.classList.add('bool-on')\n elem.addEventListener('click', () => {\n const propname = elem.dataset['property']\n elem.value = !elem.value\n elem.classList.toggle('bool-on')\n if (!value_element)\n throw new TypeError('Property name element was null.')\n\n value_element.textContent = elem.value ? 'true' : 'false'\n innerChange(propname, elem.value)\n })\n } else if (type == 'string' || type == 'number') {\n if (!value_element)\n throw new TypeError('Property name element was null.')\n value_element.setAttribute('contenteditable', 'true')\n value_element.addEventListener('keydown', function (e) {\n // allow for multiline\n if (e.code == 'Enter' && (type != 'string' || !e.shiftKey)) {\n e.preventDefault()\n this.blur()\n }\n })\n value_element.addEventListener('blur', function () {\n let v: string | number | null = this.textContent\n const propname = this.parentElement?.dataset['property']\n const proptype = this.parentElement?.dataset['type']\n if (proptype == 'number') v = Number(v)\n innerChange(propname, v)\n })\n } else if (type == 'enum' || type == 'combo') {\n const str_value = LGraphCanvas.getPropertyPrintableValue(\n value,\n options.values\n )\n if (!value_element)\n throw new TypeError('Property name element was null.')\n value_element.textContent = str_value ?? ''\n\n value_element.addEventListener('click', function (event) {\n const values = options?.values || []\n const propname = this.parentElement?.dataset['property']\n const inner_clicked = (v?: string) => {\n this.textContent = v ?? null\n innerChange(propname, v)\n return false\n }\n new LiteGraph.ContextMenu(values, {\n event,\n className: 'dark',\n callback: inner_clicked\n })\n })\n }\n\n root.content.append(elem)\n\n function innerChange(name: string | undefined, value: TWidgetValue) {\n const opts = options || {}\n opts.callback?.(name, value, opts)\n callback?.(name, value, opts)\n }\n\n return elem\n }\n\n if (typeof root.onOpen == 'function') root.onOpen()\n\n return root\n }\n\n closePanels(): void {\n type MightHaveClose = HTMLDivElement & Partial<ICloseable>\n document.querySelector<MightHaveClose>('#node-panel')?.close?.()\n document.querySelector<MightHaveClose>('#option-panel')?.close?.()\n }\n\n showShowNodePanel(node: LGraphNode): void {\n this.SELECTED_NODE = node\n this.closePanels()\n const ref_window = this.getCanvasWindow()\n const panel = this.createPanel(node.title || '', {\n closable: true,\n window: ref_window,\n onOpen: () => {\n this.NODEPANEL_IS_OPEN = true\n },\n onClose: () => {\n this.NODEPANEL_IS_OPEN = false\n this.node_panel = undefined\n }\n })\n this.node_panel = panel\n panel.id = 'node-panel'\n panel.node = node\n panel.classList.add('settings')\n panel.style.position = 'absolute'\n panel.style.top = '96px'\n panel.style.left = '65px'\n\n const inner_refresh = () => {\n // clear\n panel.content.innerHTML = ''\n panel.addHTML(\n // @ts-expect-error - desc property\n `<span class='node_type'>${node.type}</span><span class='node_desc'>${node.constructor.desc || ''}</span><span class='separator'></span>`\n )\n\n panel.addHTML('<h3>Properties</h3>')\n\n const fUpdate: PanelWidgetCallback = (name, value) => {\n if (!this.graph) throw new NullGraphError()\n if (!name) return\n this.graph.beforeChange(node)\n switch (name) {\n case 'Title':\n if (typeof value !== 'string')\n throw new TypeError(\n 'Attempting to set title to non-string value.'\n )\n\n node.title = value\n break\n case 'Mode': {\n if (typeof value !== 'string')\n throw new TypeError('Attempting to set mode to non-string value.')\n\n const kV = Object.values(LiteGraph.NODE_MODES).indexOf(value)\n if (kV !== -1 && LiteGraph.NODE_MODES[kV]) {\n node.changeMode(kV)\n } else {\n console.warn(`unexpected mode: ${value}`)\n }\n break\n }\n case 'Color':\n if (typeof value !== 'string')\n throw new TypeError(\n 'Attempting to set colour to non-string value.'\n )\n\n if (LGraphCanvas.node_colors[value]) {\n node.color = LGraphCanvas.node_colors[value].color\n node.bgcolor = LGraphCanvas.node_colors[value].bgcolor\n } else {\n console.warn(`unexpected color: ${value}`)\n }\n break\n default:\n node.setProperty(name, value)\n break\n }\n this.graph.afterChange()\n this.dirty_canvas = true\n }\n\n panel.addWidget('string', 'Title', node.title, {}, fUpdate)\n\n const mode =\n node.mode == null ? undefined : LiteGraph.NODE_MODES[node.mode]\n panel.addWidget(\n 'combo',\n 'Mode',\n mode,\n { values: LiteGraph.NODE_MODES },\n fUpdate\n )\n\n const nodeCol =\n node.color !== undefined\n ? Object.keys(LGraphCanvas.node_colors).filter(function (nK) {\n return LGraphCanvas.node_colors[nK].color == node.color\n })\n : ''\n\n panel.addWidget(\n 'combo',\n 'Color',\n nodeCol,\n { values: Object.keys(LGraphCanvas.node_colors) },\n fUpdate\n )\n\n for (const pName in node.properties) {\n const value = node.properties[pName]\n const info = node.getPropertyInfo(pName)\n\n // in case the user wants control over the side panel widget\n if (node.onAddPropertyToPanel?.(pName, panel)) continue\n\n panel.addWidget(info.widget || info.type, pName, value, info, fUpdate)\n }\n\n panel.addSeparator()\n\n node.onShowCustomPanelInfo?.(panel)\n\n // clear\n panel.footer.innerHTML = ''\n panel\n .addButton('Delete', function () {\n if (node.block_delete) return\n if (!node.graph) throw new NullGraphError()\n\n node.graph.remove(node)\n panel.close()\n })\n .classList.add('delete')\n }\n\n panel.inner_showCodePad = function (propname: string) {\n panel.classList.remove('settings')\n panel.classList.add('centered')\n\n panel.alt_content.innerHTML = \"<textarea class='code'></textarea>\"\n const textarea: HTMLTextAreaElement =\n panel.alt_content.querySelector('textarea')!\n const fDoneWith = function () {\n panel.toggleAltContent(false)\n panel.toggleFooterVisibility(true)\n textarea.remove()\n panel.classList.add('settings')\n panel.classList.remove('centered')\n inner_refresh()\n }\n textarea.value = String(node.properties[propname])\n textarea.addEventListener('keydown', function (e: KeyboardEvent) {\n if (e.code == 'Enter' && e.ctrlKey) {\n node.setProperty(propname, textarea.value)\n fDoneWith()\n }\n })\n panel.toggleAltContent(true)\n panel.toggleFooterVisibility(false)\n textarea.style.height = 'calc(100% - 40px)'\n\n const assign = panel.addButton('Assign', function () {\n node.setProperty(propname, textarea.value)\n fDoneWith()\n })\n panel.alt_content.append(assign)\n const button = panel.addButton('Close', fDoneWith)\n button.style.float = 'right'\n panel.alt_content.append(button)\n }\n\n inner_refresh()\n\n if (!this.canvas.parentNode)\n throw new TypeError('showNodePanel - this.canvas.parentNode was null')\n this.canvas.parentNode.append(panel)\n }\n\n checkPanels(): void {\n if (!this.canvas) return\n\n if (!this.canvas.parentNode)\n throw new TypeError('checkPanels - this.canvas.parentNode was null')\n const panels = this.canvas.parentNode.querySelectorAll('.litegraph.dialog')\n for (const panel of panels) {\n // @ts-expect-error Panel\n if (!panel.node) continue\n // @ts-expect-error Panel\n if (!panel.node.graph || panel.graph != this.graph) panel.close()\n }\n }\n\n getCanvasMenuOptions(): (IContextMenuValue | null)[] {\n let options: (IContextMenuValue<string> | null)[]\n if (this.getMenuOptions) {\n options = this.getMenuOptions()\n } else {\n options = [\n {\n content: 'Add Node',\n has_submenu: true,\n callback: LGraphCanvas.onMenuAdd\n },\n { content: 'Add Group', callback: LGraphCanvas.onGroupAdd },\n {\n content: 'Paste',\n callback: () => {\n this.pasteFromClipboard()\n }\n }\n // { content: \"Arrange\", callback: that.graph.arrange },\n // {content:\"Collapse All\", callback: LGraphCanvas.onMenuCollapseAll }\n ]\n if (Object.keys(this.selected_nodes).length > 1) {\n options.push(\n {\n content: 'Convert to Subgraph',\n callback: () => {\n if (!this.selectedItems.size)\n throw new Error('Convert to Subgraph: Nothing selected.')\n this._graph.convertToSubgraph(this.selectedItems)\n }\n },\n {\n content: 'Align',\n has_submenu: true,\n callback: LGraphCanvas.onGroupAlign\n }\n )\n }\n }\n\n const extra = this.getExtraMenuOptions?.(this, options)\n return Array.isArray(extra) ? options.concat(extra) : options\n }\n\n // called by processContextMenu to extract the menu list\n getNodeMenuOptions(node: LGraphNode) {\n let options: (\n | IContextMenuValue<string>\n | IContextMenuValue<string | null>\n | IContextMenuValue<INodeSlotContextItem>\n | IContextMenuValue<unknown, LGraphNode>\n | IContextMenuValue<(typeof LiteGraph.VALID_SHAPES)[number]>\n | null\n )[]\n if (node.getMenuOptions) {\n options = node.getMenuOptions(this)\n } else {\n options = [\n {\n content: 'Convert to Subgraph',\n callback: () => {\n // find groupnodes, degroup and select children\n if (this.selectedItems.size) {\n let hasGroups = false\n for (const item of this.selectedItems) {\n const node = item as LGraphNode\n const isGroup =\n typeof node.type === 'string' &&\n node.type.startsWith(`${PREFIX}${SEPARATOR}`)\n if (isGroup && node.convertToNodes) {\n hasGroups = true\n const nodes = node.convertToNodes()\n\n requestAnimationFrame(() => {\n this.selectItems(nodes, true)\n\n if (!this.selectedItems.size)\n throw new Error('Convert to Subgraph: Nothing selected.')\n this._graph.convertToSubgraph(this.selectedItems)\n })\n return\n }\n }\n\n // If no groups were found, continue normally\n if (!hasGroups) {\n if (!this.selectedItems.size)\n throw new Error('Convert to Subgraph: Nothing selected.')\n this._graph.convertToSubgraph(this.selectedItems)\n }\n } else {\n throw new Error('Convert to Subgraph: Nothing selected.')\n }\n }\n },\n {\n content: 'Properties',\n has_submenu: true,\n callback: LGraphCanvas.onShowMenuNodeProperties\n },\n {\n content: 'Properties Panel',\n callback: function (\n _item: Positionable,\n _options: IContextMenuOptions | undefined,\n _e: MouseEvent | undefined,\n _menu: ContextMenu<unknown> | undefined,\n node: LGraphNode\n ) {\n LGraphCanvas.active_canvas.showShowNodePanel(node)\n }\n },\n null,\n {\n content: 'Title',\n callback: LGraphCanvas.onShowPropertyEditor\n },\n {\n content: 'Mode',\n has_submenu: true,\n callback: LGraphCanvas.onMenuNodeMode\n }\n ]\n if (node.resizable !== false) {\n options.push({\n content: 'Resize',\n callback: LGraphCanvas.onMenuResizeNode\n })\n }\n if (node.collapsible) {\n options.push({\n content: node.collapsed ? 'Expand' : 'Collapse',\n callback: LGraphCanvas.onMenuNodeCollapse\n })\n }\n if (node.widgets?.some((w) => w.advanced)) {\n options.push({\n content: node.showAdvanced ? 'Hide Advanced' : 'Show Advanced',\n callback: LGraphCanvas.onMenuToggleAdvanced\n })\n }\n options.push(\n {\n content: node.pinned ? 'Unpin' : 'Pin',\n callback: () => {\n for (const i in this.selected_nodes) {\n const node = this.selected_nodes[i]\n node.pin()\n }\n this.setDirty(true, true)\n }\n },\n {\n content: 'Colors',\n has_submenu: true,\n callback: LGraphCanvas.onMenuNodeColors\n },\n {\n content: 'Shapes',\n has_submenu: true,\n callback: LGraphCanvas.onMenuNodeShapes\n },\n null\n )\n }\n\n const extra = node.getExtraMenuOptions?.(this, options)\n if (Array.isArray(extra) && extra.length > 0) {\n extra.push(null)\n options = extra.concat(options)\n }\n\n if (node.clonable !== false) {\n options.push({\n content: 'Clone',\n callback: LGraphCanvas.onMenuNodeClone\n })\n }\n\n if (Object.keys(this.selected_nodes).length > 1) {\n options.push(\n {\n content: 'Align Selected To',\n has_submenu: true,\n callback: LGraphCanvas.onNodeAlign\n },\n {\n content: 'Distribute Nodes',\n has_submenu: true,\n callback: LGraphCanvas.createDistributeMenu\n }\n )\n }\n\n options.push(null, {\n content: 'Remove',\n disabled: !(node.removable !== false && !node.block_delete),\n callback: LGraphCanvas.onMenuNodeRemove\n })\n\n node.graph?.onGetNodeMenuOptions?.(options, node)\n\n return options\n }\n\n /** @deprecated */\n getGroupMenuOptions(group: LGraphGroup) {\n console.warn(\n 'LGraphCanvas.getGroupMenuOptions is deprecated, use LGraphGroup.getMenuOptions instead'\n )\n return group.getMenuOptions()\n }\n\n processContextMenu(\n node: LGraphNode | undefined,\n event: CanvasPointerEvent\n ): void {\n // TODO: Remove type kludge\n let menu_info: (IContextMenuValue | string | null)[]\n const options: IContextMenuOptions = {\n event,\n callback: inner_option_clicked,\n extra: node\n }\n\n if (node) {\n options.title = node.displayType ?? node.type ?? undefined\n LGraphCanvas.active_node = node\n\n // check if mouse is in input\n const slot = node.getSlotInPosition(event.canvasX, event.canvasY)\n if (slot) {\n // on slot\n menu_info = []\n if (node.getSlotMenuOptions) {\n menu_info = node.getSlotMenuOptions(slot)\n } else {\n if (slot.output?.links?.length || slot.input?.link != null) {\n menu_info.push({ content: 'Disconnect Links', slot })\n }\n\n const _slot = slot.input || slot.output\n if (!_slot)\n throw new TypeError(\n 'Both in put and output slots were null when processing context menu.'\n )\n\n if (!_slot.nameLocked && !('link' in _slot && _slot.widget)) {\n menu_info.push({ content: 'Rename Slot', slot })\n }\n\n if (_slot.removable) {\n menu_info.push(null)\n menu_info.push(\n _slot.locked\n ? 'Cannot remove'\n : { content: 'Remove Slot', slot, className: 'danger' }\n )\n }\n\n if (node.getExtraSlotMenuOptions) {\n menu_info.push(...node.getExtraSlotMenuOptions(slot))\n }\n }\n // @ts-expect-error Slot type can be number and has number checks\n options.title = (slot.input ? slot.input.type : slot.output.type) || '*'\n if (slot.input && slot.input.type == LiteGraph.ACTION)\n options.title = 'Action'\n\n if (slot.output && slot.output.type == LiteGraph.EVENT)\n options.title = 'Event'\n } else {\n // on node\n menu_info = this.getNodeMenuOptions(node)\n }\n } else {\n menu_info = this.getCanvasMenuOptions()\n if (!this.graph) throw new NullGraphError()\n\n // Check for reroutes\n if (this.links_render_mode !== LinkRenderType.HIDDEN_LINK) {\n // Try layout store first, fallback to old method\n const rerouteLayout = layoutStore.queryRerouteAtPoint({\n x: event.canvasX,\n y: event.canvasY\n })\n\n let reroute: Reroute | undefined\n if (rerouteLayout) {\n reroute = this.graph.getReroute(rerouteLayout.id)\n } else {\n reroute = this.graph.getRerouteOnPos(\n event.canvasX,\n event.canvasY,\n this.#visibleReroutes\n )\n }\n if (reroute) {\n menu_info.unshift(\n {\n content: 'Delete Reroute',\n callback: () => {\n if (!this.graph) throw new NullGraphError()\n\n this.graph.removeReroute(reroute.id)\n }\n },\n null\n )\n }\n }\n\n const group = this.graph.getGroupOnPos(event.canvasX, event.canvasY)\n if (group) {\n // on group\n menu_info.push(null, {\n content: 'Edit Group',\n has_submenu: true,\n submenu: {\n title: 'Group',\n extra: group,\n options: group.getMenuOptions()\n }\n })\n }\n }\n\n // show menu\n if (!menu_info) return\n\n new LiteGraph.ContextMenu(menu_info, options)\n\n const createDialog = (options: IDialogOptions) =>\n this.createDialog(\n \"<span class='name'>Name</span><input autofocus type='text'/><button>OK</button>\",\n options\n )\n const setDirty = () => this.setDirty(true)\n\n function inner_option_clicked(\n v: IContextMenuValue<unknown>,\n options: IDialogOptions\n ) {\n if (!v) return\n\n if (v.content == 'Remove Slot') {\n if (!node?.graph) throw new NullGraphError()\n\n const info = v.slot\n if (!info)\n throw new TypeError(\n 'Found-slot info was null when processing context menu.'\n )\n\n node.graph.beforeChange()\n if (info.input) {\n node.removeInput(info.slot)\n } else if (info.output) {\n node.removeOutput(info.slot)\n }\n node.graph.afterChange()\n return\n } else if (v.content == 'Disconnect Links') {\n if (!node?.graph) throw new NullGraphError()\n\n const info = v.slot\n if (!info)\n throw new TypeError(\n 'Found-slot info was null when processing context menu.'\n )\n\n node.graph.beforeChange()\n if (info.output) {\n node.disconnectOutput(info.slot)\n } else if (info.input) {\n node.disconnectInput(info.slot, true)\n }\n node.graph.afterChange()\n return\n } else if (v.content == 'Rename Slot') {\n if (!node)\n throw new TypeError(\n '`node` was null when processing the context menu.'\n )\n\n const info = v.slot\n if (!info)\n throw new TypeError(\n 'Found-slot info was null when processing context menu.'\n )\n\n const slot_info = info.input\n ? node.getInputInfo(info.slot)\n : node.getOutputInfo(info.slot)\n const dialog = createDialog(options)\n\n const input = dialog.querySelector('input')\n if (input && slot_info) {\n input.value = slot_info.label || ''\n }\n const inner = function () {\n if (!node.graph) throw new NullGraphError()\n\n node.graph.beforeChange()\n if (input?.value) {\n if (slot_info) {\n slot_info.label = input.value\n }\n setDirty()\n }\n dialog.close()\n node.graph.afterChange()\n }\n dialog.querySelector('button')?.addEventListener('click', inner)\n if (!input)\n throw new TypeError(\n 'Input element was null when processing context menu.'\n )\n\n input.addEventListener('keydown', function (e) {\n dialog.is_modified = true\n if (e.key == 'Escape') {\n // ESC\n dialog.close()\n } else if (e.key == 'Enter') {\n // save\n inner()\n } else if ((e.target as Element).localName != 'textarea') {\n return\n }\n e.preventDefault()\n e.stopPropagation()\n })\n input.focus()\n }\n }\n }\n\n /**\n * Starts an animation to fit the view around the specified selection of nodes.\n * @param bounds The bounds to animate the view to, defined by a rectangle.\n */\n animateToBounds(bounds: ReadOnlyRect, options: AnimationOptions = {}) {\n const setDirty = () => this.setDirty(true, true)\n this.ds.animateToBounds(bounds, setDirty, options)\n }\n\n /**\n * Fits the view to the selected nodes with animation.\n * If nothing is selected, the view is fitted around all items in the graph.\n */\n fitViewToSelectionAnimated(options: AnimationOptions = {}) {\n const items = this.selectedItems.size\n ? Array.from(this.selectedItems)\n : this.positionableItems\n const bounds = createBounds(items)\n if (!bounds)\n throw new TypeError(\n 'Attempted to fit to view but could not calculate bounds.'\n )\n\n const setDirty = () => this.setDirty(true, true)\n this.ds.animateToBounds(bounds, setDirty, options)\n }\n\n /**\n * Calculate new position with delta\n */\n private calculateNewPosition(\n node: LGraphNode,\n deltaX: number,\n deltaY: number\n ): { x: number; y: number } {\n return {\n x: node.pos[0] + deltaX,\n y: node.pos[1] + deltaY\n }\n }\n\n /**\n * Apply batched node position updates\n */\n private applyNodePositionUpdates(\n nodesToMove: Array<{ node: LGraphNode; newPos: { x: number; y: number } }>,\n mutations: ReturnType<typeof useLayoutMutations>\n ): void {\n for (const { node, newPos } of nodesToMove) {\n // Update LiteGraph position first so next drag uses correct base position\n node.pos[0] = newPos.x\n node.pos[1] = newPos.y\n // Then update layout store which will update Vue nodes\n mutations.moveNode(node.id, newPos)\n }\n }\n\n /**\n * Initialize layout mutations with Canvas source\n */\n private initLayoutMutations(): ReturnType<typeof useLayoutMutations> {\n const mutations = useLayoutMutations()\n mutations.setSource(LayoutSource.Canvas)\n return mutations\n }\n\n /**\n * Collect all nodes that are children of groups in the selection\n */\n private collectNodesInGroups(items: Set<Positionable>): Set<LGraphNode> {\n const nodesInGroups = new Set<LGraphNode>()\n for (const item of items) {\n if (item instanceof LGraphGroup) {\n for (const child of item._children) {\n if (child instanceof LGraphNode) {\n nodesInGroups.add(child)\n }\n }\n }\n }\n return nodesInGroups\n }\n\n /**\n * Move group children (both nodes and non-nodes)\n */\n private moveGroupChildren(\n group: LGraphGroup,\n deltaX: number,\n deltaY: number,\n nodesToMove: Array<{ node: LGraphNode; newPos: { x: number; y: number } }>\n ): void {\n for (const child of group._children) {\n if (child instanceof LGraphNode) {\n const node = child as LGraphNode\n nodesToMove.push({\n node,\n newPos: this.calculateNewPosition(node, deltaX, deltaY)\n })\n } else if (!(child instanceof LGraphGroup)) {\n // Non-node, non-group children (reroutes, etc.)\n // Skip groups here - they're already in allItems and will be\n // processed in the main loop of moveChildNodesInGroupVueMode\n child.move(deltaX, deltaY, true)\n }\n }\n }\n\n moveChildNodesInGroupVueMode(\n allItems: Set<Positionable>,\n deltaX: number,\n deltaY: number\n ) {\n const mutations = this.initLayoutMutations()\n const nodesInMovingGroups = this.collectNodesInGroups(allItems)\n const nodesToMove: NewNodePosition[] = []\n\n // First, collect all the moves we need to make\n for (const item of allItems) {\n const isNode = item instanceof LGraphNode\n if (isNode) {\n const node = item as LGraphNode\n if (nodesInMovingGroups.has(node)) {\n continue\n }\n nodesToMove.push({\n node,\n newPos: this.calculateNewPosition(node, deltaX, deltaY)\n })\n } else if (item instanceof LGraphGroup) {\n item.move(deltaX, deltaY, true)\n this.moveGroupChildren(item, deltaX, deltaY, nodesToMove)\n } else {\n // Other items (reroutes, etc.)\n item.move(deltaX, deltaY, true)\n }\n }\n\n // Now apply all the node moves at once\n this.applyNodePositionUpdates(nodesToMove, mutations)\n }\n\n repositionNodesVueMode(nodesToReposition: NewNodePosition[]) {\n const mutations = this.initLayoutMutations()\n this.applyNodePositionUpdates(nodesToReposition, mutations)\n }\n}\n","/**\n * Temporary workaround until downstream consumers migrate to Map.\n * A brittle wrapper with many flaws, but should be fine for simple maps using int indexes.\n */\nexport class MapProxyHandler<V> implements ProxyHandler<\n Map<number | string, V>\n> {\n getOwnPropertyDescriptor(\n target: Map<number | string, V>,\n p: string | symbol\n ): PropertyDescriptor | undefined {\n const value = this.get(target, p)\n if (value) {\n return {\n configurable: true,\n enumerable: true,\n value\n }\n }\n }\n\n has(target: Map<number | string, V>, p: string | symbol): boolean {\n if (typeof p === 'symbol') return false\n\n const int = parseInt(p, 10)\n return target.has(!isNaN(int) ? int : p)\n }\n\n ownKeys(target: Map<number | string, V>): ArrayLike<string | symbol> {\n return [...target.keys()].map(String)\n }\n\n get(target: Map<number | string, V>, p: string | symbol): V | undefined {\n // Workaround does not support link IDs of \"values\", \"entries\", \"constructor\", etc.\n if (p in target) return Reflect.get(target, p, target)\n if (typeof p === 'symbol') return\n\n const int = parseInt(p, 10)\n return target.get(!isNaN(int) ? int : p)\n }\n\n set(\n target: Map<number | string, V>,\n p: string | symbol,\n newValue: V\n ): boolean {\n if (typeof p === 'symbol') return false\n\n const int = parseInt(p, 10)\n target.set(!isNaN(int) ? int : p, newValue)\n return true\n }\n\n deleteProperty(target: Map<number | string, V>, p: string | symbol): boolean {\n return target.delete(p as number | string)\n }\n\n static bindAllMethods(map: Map<unknown, unknown>): void {\n map.clear = map.clear.bind(map)\n map.delete = map.delete.bind(map)\n map.forEach = map.forEach.bind(map)\n map.get = map.get.bind(map)\n map.has = map.has.bind(map)\n map.set = map.set.bind(map)\n map.entries = map.entries.bind(map)\n map.keys = map.keys.bind(map)\n map.values = map.values.bind(map)\n\n map[Symbol.iterator] = map[Symbol.iterator].bind(map)\n }\n}\n","import { toString } from 'es-toolkit/compat'\n\nimport {\n SUBGRAPH_INPUT_ID,\n SUBGRAPH_OUTPUT_ID\n} from '@/lib/litegraph/src/constants'\nimport type { UUID } from '@/lib/litegraph/src/utils/uuid'\nimport { createUuidv4, zeroUuid } from '@/lib/litegraph/src/utils/uuid'\nimport { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'\nimport { LayoutSource } from '@/renderer/core/layout/types'\n\nimport type { DragAndScaleState } from './DragAndScale'\nimport { LGraphCanvas } from './LGraphCanvas'\nimport { LGraphGroup } from './LGraphGroup'\nimport { LGraphNode } from './LGraphNode'\nimport type { NodeId } from './LGraphNode'\nimport { LLink } from './LLink'\nimport type { LinkId, SerialisedLLinkArray } from './LLink'\nimport { MapProxyHandler } from './MapProxyHandler'\nimport { Reroute } from './Reroute'\nimport type { RerouteId } from './Reroute'\nimport { CustomEventTarget } from './infrastructure/CustomEventTarget'\nimport type { LGraphEventMap } from './infrastructure/LGraphEventMap'\nimport type { SubgraphEventMap } from './infrastructure/SubgraphEventMap'\nimport type {\n DefaultConnectionColors,\n Dictionary,\n HasBoundingRect,\n IContextMenuValue,\n INodeInputSlot,\n INodeOutputSlot,\n LinkNetwork,\n LinkSegment,\n MethodNames,\n OptionalProps,\n Point,\n Positionable,\n Size\n} from './interfaces'\nimport { LiteGraph, SubgraphNode } from './litegraph'\nimport {\n alignOutsideContainer,\n alignToContainer,\n createBounds\n} from './measure'\nimport { SubgraphInput } from './subgraph/SubgraphInput'\nimport { SubgraphInputNode } from './subgraph/SubgraphInputNode'\nimport { SubgraphOutput } from './subgraph/SubgraphOutput'\nimport { SubgraphOutputNode } from './subgraph/SubgraphOutputNode'\nimport {\n findUsedSubgraphIds,\n getBoundaryLinks,\n groupResolvedByOutput,\n mapSubgraphInputsAndLinks,\n mapSubgraphOutputsAndLinks,\n multiClone,\n splitPositionables\n} from './subgraph/subgraphUtils'\nimport { Alignment, LGraphEventMode } from './types/globalEnums'\nimport type {\n LGraphTriggerAction,\n LGraphTriggerEvent,\n LGraphTriggerHandler,\n LGraphTriggerParam\n} from './types/graphTriggers'\nimport type {\n ExportedSubgraph,\n ExposedWidget,\n ISerialisedGraph,\n ISerialisedNode,\n Serialisable,\n SerialisableGraph,\n SerialisableReroute\n} from './types/serialisation'\nimport { getAllNestedItems } from './utils/collections'\n\nexport type {\n LGraphTriggerAction,\n LGraphTriggerParam\n} from './types/graphTriggers'\n\nexport type RendererType = 'LG' | 'Vue'\n\nexport interface LGraphState {\n lastGroupId: number\n lastNodeId: number\n lastLinkId: number\n lastRerouteId: number\n}\n\ntype ParamsArray<\n T extends Record<any, any>,\n K extends MethodNames<T>\n> = Parameters<T[K]>[1] extends undefined\n ? Parameters<T[K]> | Parameters<T[K]>[0]\n : Parameters<T[K]>\n\n/** Configuration used by {@link LGraph} `config`. */\nexport interface LGraphConfig {\n /** @deprecated Legacy config - unused */\n align_to_grid?: boolean\n links_ontop?: boolean\n}\n\nexport interface GroupNodeWorkflowData {\n external: (number | string)[][]\n links: SerialisedLLinkArray[]\n nodes: {\n index?: number\n type?: string\n inputs?: unknown[]\n outputs?: unknown[]\n }[]\n config?: Record<number, unknown>\n}\n\nexport interface LGraphExtra extends Dictionary<unknown> {\n reroutes?: SerialisableReroute[]\n linkExtensions?: { id: number; parentId: number | undefined }[]\n ds?: DragAndScaleState\n workflowRendererVersion?: RendererType\n groupNodes?: Record<string, GroupNodeWorkflowData>\n}\n\nexport interface BaseLGraph {\n /** The root graph. */\n readonly rootGraph: LGraph\n}\n\n/**\n * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.\n * supported callbacks:\n * + onNodeAdded: when a new node is added to the graph\n * + onNodeRemoved: when a node inside this graph is removed\n */\nexport class LGraph\n implements LinkNetwork, BaseLGraph, Serialisable<SerialisableGraph>\n{\n static serialisedSchemaVersion = 1 as const\n\n static STATUS_STOPPED = 1\n static STATUS_RUNNING = 2\n\n /** List of LGraph properties that are manually handled by {@link LGraph.configure}. */\n static readonly ConfigureProperties = new Set([\n 'nodes',\n 'groups',\n 'links',\n 'state',\n 'reroutes',\n 'floatingLinks',\n 'id',\n 'subgraphs',\n 'definitions',\n 'inputs',\n 'outputs',\n 'widgets',\n 'inputNode',\n 'outputNode',\n 'extra'\n ])\n\n id: UUID = zeroUuid\n revision: number = 0\n\n _version: number = -1\n /** The backing store for links. Keys are wrapped in String() */\n _links: Map<LinkId, LLink> = new Map()\n /**\n * Indexed property access is deprecated.\n * Backwards compatibility with a Proxy has been added, but will eventually be removed.\n *\n * Use {@link Map} methods:\n * ```\n * const linkId = 123\n * const link = graph.links.get(linkId)\n * // Deprecated: const link = graph.links[linkId]\n * ```\n */\n links: Map<LinkId, LLink> & Record<LinkId, LLink>\n list_of_graphcanvas: LGraphCanvas[] | null\n status: number = LGraph.STATUS_STOPPED\n\n state: LGraphState = {\n lastGroupId: 0,\n lastNodeId: 0,\n lastLinkId: 0,\n lastRerouteId: 0\n }\n\n readonly events = new CustomEventTarget<LGraphEventMap>()\n readonly _subgraphs: Map<UUID, Subgraph> = new Map()\n\n _nodes: (LGraphNode | SubgraphNode)[] = []\n _nodes_by_id: Record<NodeId, LGraphNode> = {}\n _nodes_in_order: LGraphNode[] = []\n _nodes_executable: LGraphNode[] | null = null\n _groups: LGraphGroup[] = []\n iteration: number = 0\n globaltime: number = 0\n /** @deprecated Unused */\n runningtime: number = 0\n fixedtime: number = 0\n fixedtime_lapse: number = 0.01\n elapsed_time: number = 0.01\n last_update_time: number = 0\n starttime: number = 0\n catch_errors: boolean = true\n execution_timer_id?: number | null\n errors_in_execution?: boolean\n /** @deprecated Unused */\n execution_time!: number\n _last_trigger_time?: number\n filter?: string\n /** Must contain serialisable values, e.g. primitive types */\n config: LGraphConfig = {}\n vars: Dictionary<unknown> = {}\n nodes_executing: boolean[] = []\n nodes_actioning: (string | boolean)[] = []\n nodes_executedAction: string[] = []\n extra: LGraphExtra = {}\n\n /** @deprecated Deserialising a workflow sets this unused property. */\n version?: number\n\n /** @returns Whether the graph has no items */\n get empty(): boolean {\n return this._nodes.length + this._groups.length + this.reroutes.size === 0\n }\n\n /** @returns All items on the canvas that can be selected */\n *positionableItems(): Generator<LGraphNode | LGraphGroup | Reroute> {\n for (const node of this._nodes) yield node\n for (const group of this._groups) yield group\n for (const reroute of this.reroutes.values()) yield reroute\n return\n }\n\n /** Internal only. Not required for serialisation; calculated on deserialise. */\n #lastFloatingLinkId: number = 0\n\n private readonly floatingLinksInternal: Map<LinkId, LLink> = new Map()\n get floatingLinks(): ReadonlyMap<LinkId, LLink> {\n return this.floatingLinksInternal\n }\n\n private readonly reroutesInternal = new Map<RerouteId, Reroute>()\n /** All reroutes in this graph. */\n public get reroutes(): Map<RerouteId, Reroute> {\n return this.reroutesInternal\n }\n\n get rootGraph(): LGraph {\n return this\n }\n\n get isRootGraph(): boolean {\n return this.rootGraph === this\n }\n\n /** @deprecated See {@link state}.{@link LGraphState.lastNodeId lastNodeId} */\n get last_node_id() {\n return this.state.lastNodeId\n }\n\n set last_node_id(value) {\n this.state.lastNodeId = value\n }\n\n /** @deprecated See {@link state}.{@link LGraphState.lastLinkId lastLinkId} */\n get last_link_id() {\n return this.state.lastLinkId\n }\n\n set last_link_id(value) {\n this.state.lastLinkId = value\n }\n\n onAfterStep?(): void\n onBeforeStep?(): void\n onPlayEvent?(): void\n onStopEvent?(): void\n onAfterExecute?(): void\n onExecuteStep?(): void\n onNodeAdded?(node: LGraphNode): void\n onNodeRemoved?(node: LGraphNode): void\n onTrigger?: LGraphTriggerHandler\n onBeforeChange?(graph: LGraph, info?: LGraphNode): void\n onAfterChange?(graph: LGraph, info?: LGraphNode | null): void\n onConnectionChange?(node: LGraphNode): void\n on_change?(graph: LGraph): void\n onSerialize?(data: ISerialisedGraph | SerialisableGraph): void\n onConfigure?(data: ISerialisedGraph | SerialisableGraph): void\n onGetNodeMenuOptions?(\n options: (IContextMenuValue<unknown> | null)[],\n node: LGraphNode\n ): void\n\n // @ts-expect-error - Private property type needs fixing\n private _input_nodes?: LGraphNode[]\n\n /**\n * See {@link LGraph}\n * @param o data from previous serialization [optional]\n */\n constructor(o?: ISerialisedGraph | SerialisableGraph) {\n /** @see MapProxyHandler */\n const links = this._links\n MapProxyHandler.bindAllMethods(links)\n const handler = new MapProxyHandler<LLink>()\n this.links = new Proxy(links, handler) as Map<LinkId, LLink> &\n Record<LinkId, LLink>\n\n this.list_of_graphcanvas = null\n this.clear()\n\n if (o) this.configure(o)\n }\n\n /**\n * Removes all nodes from this graph\n */\n clear(): void {\n this.stop()\n this.status = LGraph.STATUS_STOPPED\n\n this.id = zeroUuid\n this.revision = 0\n\n this.state = {\n lastGroupId: 0,\n lastNodeId: 0,\n lastLinkId: 0,\n lastRerouteId: 0\n }\n\n // used to detect changes\n this._version = -1\n this._subgraphs.clear()\n\n // safe clear\n if (this._nodes) {\n for (const _node of this._nodes) {\n _node.onRemoved?.()\n this.onNodeRemoved?.(_node)\n }\n }\n\n // nodes\n this._nodes = []\n this._nodes_by_id = {}\n // nodes sorted in execution order\n this._nodes_in_order = []\n // nodes that contain onExecute sorted in execution order\n this._nodes_executable = null\n\n this._links.clear()\n this.reroutes.clear()\n this.floatingLinksInternal.clear()\n\n this.#lastFloatingLinkId = 0\n\n // other scene stuff\n this._groups = []\n\n // iterations\n this.iteration = 0\n\n // custom data\n this.config = {}\n this.vars = {}\n // to store custom data\n this.extra = {}\n\n // timing\n this.globaltime = 0\n this.runningtime = 0\n this.fixedtime = 0\n this.fixedtime_lapse = 0.01\n this.elapsed_time = 0.01\n this.last_update_time = 0\n this.starttime = 0\n\n this.catch_errors = true\n\n this.nodes_executing = []\n this.nodes_actioning = []\n this.nodes_executedAction = []\n\n // notify canvas to redraw\n this.change()\n\n this.canvasAction((c) => c.clear())\n }\n\n get subgraphs(): Map<UUID, Subgraph> {\n return this.rootGraph._subgraphs\n }\n\n get nodes() {\n return this._nodes\n }\n\n get groups() {\n return this._groups\n }\n\n /**\n * Attach Canvas to this graph\n */\n attachCanvas(canvas: LGraphCanvas): void {\n if (!(canvas instanceof LGraphCanvas)) {\n throw new TypeError('attachCanvas expects an LGraphCanvas instance')\n }\n\n this.primaryCanvas = canvas\n\n this.list_of_graphcanvas ??= []\n if (!this.list_of_graphcanvas.includes(canvas)) {\n this.list_of_graphcanvas.push(canvas)\n }\n\n if (canvas.graph === this) return\n\n canvas.graph?.detachCanvas(canvas)\n canvas.graph = this\n canvas.subgraph = undefined\n }\n\n /**\n * Detach Canvas from this graph\n */\n detachCanvas(canvas: LGraphCanvas): void {\n canvas.graph = null\n const canvases = this.list_of_graphcanvas\n if (canvases) {\n const pos = canvases.indexOf(canvas)\n if (pos !== -1) canvases.splice(pos, 1)\n }\n }\n\n /**\n * @deprecated Will be removed in 0.9\n * Starts running this graph every interval milliseconds.\n * @param interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate\n */\n start(interval?: number): void {\n if (this.status == LGraph.STATUS_RUNNING) return\n this.status = LGraph.STATUS_RUNNING\n\n this.onPlayEvent?.()\n this.sendEventToAllNodes('onStart')\n\n // launch\n this.starttime = LiteGraph.getTime()\n this.last_update_time = this.starttime\n interval ||= 0\n\n // execute once per frame\n if (\n interval == 0 &&\n typeof window != 'undefined' &&\n window.requestAnimationFrame\n ) {\n const on_frame = () => {\n if (this.execution_timer_id != -1) return\n\n window.requestAnimationFrame(on_frame)\n this.onBeforeStep?.()\n this.runStep(1, !this.catch_errors)\n this.onAfterStep?.()\n }\n this.execution_timer_id = -1\n on_frame()\n } else {\n // execute every 'interval' ms\n // @ts-expect-error - Timer ID type mismatch needs fixing\n this.execution_timer_id = setInterval(() => {\n // execute\n this.onBeforeStep?.()\n this.runStep(1, !this.catch_errors)\n this.onAfterStep?.()\n }, interval)\n }\n }\n\n /**\n * @deprecated Will be removed in 0.9\n * Stops the execution loop of the graph\n */\n stop(): void {\n if (this.status == LGraph.STATUS_STOPPED) return\n\n this.status = LGraph.STATUS_STOPPED\n\n this.onStopEvent?.()\n\n if (this.execution_timer_id != null) {\n if (this.execution_timer_id != -1) {\n clearInterval(this.execution_timer_id)\n }\n this.execution_timer_id = null\n }\n\n this.sendEventToAllNodes('onStop')\n }\n\n /**\n * Run N steps (cycles) of the graph\n * @param num number of steps to run, default is 1\n * @param do_not_catch_errors [optional] if you want to try/catch errors\n * @param limit max number of nodes to execute (used to execute from start to a node)\n */\n runStep(num: number, do_not_catch_errors: boolean, limit?: number): void {\n num = num || 1\n\n const start = LiteGraph.getTime()\n this.globaltime = 0.001 * (start - this.starttime)\n\n const nodes = this._nodes_executable || this._nodes\n if (!nodes) return\n\n limit = limit || nodes.length\n\n if (do_not_catch_errors) {\n // iterations\n for (let i = 0; i < num; i++) {\n for (let j = 0; j < limit; ++j) {\n const node = nodes[j]\n // FIXME: Looks like copy/paste broken logic - checks for \"on\", executes \"do\"\n if (node.mode == LGraphEventMode.ALWAYS && node.onExecute) {\n // wrap node.onExecute();\n node.doExecute?.()\n }\n }\n\n this.fixedtime += this.fixedtime_lapse\n this.onExecuteStep?.()\n }\n\n this.onAfterExecute?.()\n } else {\n try {\n // iterations\n for (let i = 0; i < num; i++) {\n for (let j = 0; j < limit; ++j) {\n const node = nodes[j]\n if (node.mode == LGraphEventMode.ALWAYS) {\n node.onExecute?.()\n }\n }\n\n this.fixedtime += this.fixedtime_lapse\n this.onExecuteStep?.()\n }\n\n this.onAfterExecute?.()\n this.errors_in_execution = false\n } catch (error) {\n this.errors_in_execution = true\n if (LiteGraph.throw_errors) throw error\n\n if (LiteGraph.debug) console.error('Error during execution:', error)\n this.stop()\n }\n }\n\n const now = LiteGraph.getTime()\n let elapsed = now - start\n if (elapsed == 0) elapsed = 1\n\n this.execution_time = 0.001 * elapsed\n this.globaltime += 0.001 * elapsed\n this.iteration += 1\n this.elapsed_time = (now - this.last_update_time) * 0.001\n this.last_update_time = now\n this.nodes_executing = []\n this.nodes_actioning = []\n this.nodes_executedAction = []\n }\n\n /**\n * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than\n * nodes with only inputs.\n */\n updateExecutionOrder(): void {\n this._nodes_in_order = this.computeExecutionOrder(false)\n this._nodes_executable = []\n for (const node of this._nodes_in_order) {\n if (node.onExecute) {\n this._nodes_executable.push(node)\n }\n }\n }\n\n // This is more internal, it computes the executable nodes in order and returns it\n computeExecutionOrder(\n only_onExecute: boolean,\n set_level?: boolean\n ): LGraphNode[] {\n const L: LGraphNode[] = []\n const S: LGraphNode[] = []\n const M: Dictionary<LGraphNode> = {}\n // to avoid repeating links\n const visited_links: Record<NodeId, boolean> = {}\n const remaining_links: Record<NodeId, number> = {}\n\n // search for the nodes without inputs (starting nodes)\n for (const node of this._nodes) {\n if (only_onExecute && !node.onExecute) {\n continue\n }\n\n // add to pending nodes\n M[node.id] = node\n\n // num of input connections\n let num = 0\n if (node.inputs) {\n for (const input of node.inputs) {\n if (input?.link != null) {\n num += 1\n }\n }\n }\n\n if (num == 0) {\n // is a starting node\n S.push(node)\n if (set_level) node._level = 1\n } else {\n // num of input links\n if (set_level) node._level = 0\n remaining_links[node.id] = num\n }\n }\n\n while (true) {\n // get an starting node\n const node = S.shift()\n if (node === undefined) break\n\n // add to ordered list\n L.push(node)\n // remove from the pending nodes\n delete M[node.id]\n\n if (!node.outputs) continue\n\n // for every output\n for (const output of node.outputs) {\n // not connected\n // TODO: Confirm functionality, clean condition\n if (output?.links == null || output.links.length == 0) continue\n\n // for every connection\n for (const link_id of output.links) {\n const link = this._links.get(link_id)\n if (!link) continue\n\n // already visited link (ignore it)\n if (visited_links[link.id]) continue\n\n const target_node = this.getNodeById(link.target_id)\n if (target_node == null) {\n visited_links[link.id] = true\n continue\n }\n\n if (set_level) {\n node._level ??= 0\n if (!target_node._level || target_node._level <= node._level) {\n target_node._level = node._level + 1\n }\n }\n\n // mark as visited\n visited_links[link.id] = true\n // reduce the number of links remaining\n remaining_links[target_node.id] -= 1\n\n // if no more links, then add to starters array\n if (remaining_links[target_node.id] == 0) S.push(target_node)\n }\n }\n }\n\n // the remaining ones (loops)\n for (const i in M) {\n L.push(M[i])\n }\n\n if (L.length != this._nodes.length && LiteGraph.debug)\n console.warn('something went wrong, nodes missing')\n\n /** Ensure type is set */\n type OrderedLGraphNode = LGraphNode & { order: number }\n\n /** Sets the order property of each provided node to its index in {@link nodes}. */\n function setOrder(\n nodes: LGraphNode[]\n ): asserts nodes is OrderedLGraphNode[] {\n const l = nodes.length\n for (let i = 0; i < l; ++i) {\n nodes[i].order = i\n }\n }\n\n // save order number in the node\n setOrder(L)\n\n // sort now by priority\n L.sort(function (A, B) {\n // @ts-expect-error ctor props\n const Ap = A.constructor.priority || A.priority || 0\n // @ts-expect-error ctor props\n const Bp = B.constructor.priority || B.priority || 0\n // if same priority, sort by order\n\n return Ap == Bp ? A.order - B.order : Ap - Bp\n })\n\n // save order number in the node, again...\n setOrder(L)\n\n return L\n }\n\n /**\n * Positions every node in a more readable manner\n */\n arrange(margin?: number, layout?: string): void {\n margin = margin || 100\n\n const nodes = this.computeExecutionOrder(false, true)\n const columns: LGraphNode[][] = []\n for (const node of nodes) {\n const col = node._level || 1\n columns[col] ||= []\n columns[col].push(node)\n }\n\n let x = margin\n\n for (const column of columns) {\n if (!column) continue\n\n let max_size = 100\n let y = margin + LiteGraph.NODE_TITLE_HEIGHT\n for (const node of column) {\n node.pos[0] = layout == LiteGraph.VERTICAL_LAYOUT ? y : x\n node.pos[1] = layout == LiteGraph.VERTICAL_LAYOUT ? x : y\n const max_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 1 : 0\n if (node.size[max_size_index] > max_size) {\n max_size = node.size[max_size_index]\n }\n const node_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 0 : 1\n y += node.size[node_size_index] + margin + LiteGraph.NODE_TITLE_HEIGHT\n }\n x += max_size + margin\n }\n\n this.setDirtyCanvas(true, true)\n }\n\n /**\n * Returns the amount of time the graph has been running in milliseconds\n * @returns number of milliseconds the graph has been running\n */\n getTime(): number {\n return this.globaltime\n }\n\n /**\n * Returns the amount of time accumulated using the fixedtime_lapse var.\n * This is used in context where the time increments should be constant\n * @returns number of milliseconds the graph has been running\n */\n getFixedTime(): number {\n return this.fixedtime\n }\n\n /**\n * Returns the amount of time it took to compute the latest iteration.\n * Take into account that this number could be not correct\n * if the nodes are using graphical actions\n * @returns number of milliseconds it took the last cycle\n */\n getElapsedTime(): number {\n return this.elapsed_time\n }\n\n /**\n * @deprecated Will be removed in 0.9\n * Sends an event to all the nodes, useful to trigger stuff\n * @param eventname the name of the event (function to be called)\n * @param params parameters in array format\n */\n sendEventToAllNodes(\n eventname: string,\n params?: object | object[],\n mode?: LGraphEventMode\n ): void {\n mode = mode || LGraphEventMode.ALWAYS\n\n const nodes = this._nodes_in_order || this._nodes\n if (!nodes) return\n\n for (const node of nodes) {\n // @ts-expect-error deprecated\n if (!node[eventname] || node.mode != mode) continue\n if (params === undefined) {\n // @ts-expect-error deprecated\n node[eventname]()\n } else if (params && params.constructor === Array) {\n // @ts-expect-error deprecated\n // eslint-disable-next-line prefer-spread\n node[eventname].apply(node, params)\n } else {\n // @ts-expect-error deprecated\n node[eventname](params)\n }\n }\n }\n\n /**\n * Runs an action on every canvas registered to this graph.\n * @param action Action to run for every canvas\n */\n canvasAction(action: (canvas: LGraphCanvas) => void): void {\n const canvases = this.list_of_graphcanvas\n if (!canvases) return\n for (const canvas of canvases) action(canvas)\n }\n\n /** @deprecated See {@link LGraph.canvasAction} */\n sendActionToCanvas<T extends MethodNames<LGraphCanvas>>(\n action: T,\n params?: ParamsArray<LGraphCanvas, T>\n ): void {\n const { list_of_graphcanvas } = this\n if (!list_of_graphcanvas) return\n\n for (const c of list_of_graphcanvas) {\n const method = c[action]\n\n if (typeof method === 'function') {\n const args =\n params == null ? [] : Array.isArray(params) ? params : [params]\n ;(method as (...args: unknown[]) => unknown).apply(c, args)\n }\n }\n }\n\n /**\n * Adds a new node instance to this graph\n * @param node the instance of the node\n */\n add(\n node: LGraphNode | LGraphGroup,\n skip_compute_order?: boolean\n ): LGraphNode | null | undefined {\n if (!node) return\n const { state } = this\n\n // Ensure created items are snapped\n if (LiteGraph.alwaysSnapToGrid) {\n const snapTo = this.getSnapToGridSize()\n if (snapTo) node.snapToGrid(snapTo)\n }\n\n // LEGACY: This was changed from constructor === LGraphGroup\n // groups\n if (node instanceof LGraphGroup) {\n // Assign group ID\n if (node.id == null || node.id === -1) node.id = ++state.lastGroupId\n if (node.id > state.lastGroupId) state.lastGroupId = node.id\n\n this._groups.push(node)\n this.setDirtyCanvas(true)\n this.change()\n node.graph = this\n this._version++\n return\n }\n\n // nodes\n if (node.id != -1 && this._nodes_by_id[node.id] != null) {\n console.warn(\n 'LiteGraph: there is already a node with this ID, changing it'\n )\n node.id = LiteGraph.use_uuids ? LiteGraph.uuidv4() : ++state.lastNodeId\n }\n\n if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) {\n throw 'LiteGraph: max number of nodes in a graph reached'\n }\n\n // give him an id\n if (LiteGraph.use_uuids) {\n if (node.id == null || node.id == -1) node.id = LiteGraph.uuidv4()\n } else {\n if (node.id == null || node.id == -1) {\n node.id = ++state.lastNodeId\n } else if (typeof node.id === 'number' && state.lastNodeId < node.id) {\n state.lastNodeId = node.id\n }\n }\n\n node.graph = this\n this._version++\n\n this._nodes.push(node)\n this._nodes_by_id[node.id] = node\n\n node.onAdded?.(this)\n\n if (this.config.align_to_grid) node.alignToGrid()\n\n if (!skip_compute_order) this.updateExecutionOrder()\n\n this.onNodeAdded?.(node)\n\n this.setDirtyCanvas(true)\n this.change()\n\n // to chain actions\n return node\n }\n\n /**\n * Removes a node from the graph\n * @param node the instance of the node\n */\n remove(node: LGraphNode | LGraphGroup): void {\n // LEGACY: This was changed from constructor === LiteGraph.LGraphGroup\n if (node instanceof LGraphGroup) {\n this.canvasAction((c) => c.deselect(node))\n\n const index = this._groups.indexOf(node)\n if (index != -1) {\n this._groups.splice(index, 1)\n }\n node.graph = undefined\n this._version++\n this.setDirtyCanvas(true, true)\n this.change()\n return\n }\n\n // not found\n if (this._nodes_by_id[node.id] == null) {\n console.warn('LiteGraph: node not found', node)\n return\n }\n // cannot be removed\n if (node.ignore_remove) {\n console.warn('LiteGraph: node cannot be removed', node)\n return\n }\n\n // sure? - almost sure is wrong\n this.beforeChange()\n\n const { inputs, outputs } = node\n\n // disconnect inputs\n if (inputs) {\n for (const [i, slot] of inputs.entries()) {\n if (slot.link != null) node.disconnectInput(i, true)\n }\n }\n\n // disconnect outputs\n if (outputs) {\n for (const [i, slot] of outputs.entries()) {\n if (slot.links?.length) node.disconnectOutput(i)\n }\n }\n\n // Floating links\n for (const link of this.floatingLinks.values()) {\n if (link.origin_id === node.id || link.target_id === node.id) {\n this.removeFloatingLink(link)\n }\n }\n\n // callback\n node.onRemoved?.()\n\n node.graph = null\n this._version++\n\n // remove from canvas render\n const { list_of_graphcanvas } = this\n if (list_of_graphcanvas) {\n for (const canvas of list_of_graphcanvas) {\n if (canvas.selected_nodes[node.id])\n delete canvas.selected_nodes[node.id]\n\n canvas.deselect(node)\n }\n }\n\n // remove from containers\n const pos = this._nodes.indexOf(node)\n if (pos != -1) this._nodes.splice(pos, 1)\n\n delete this._nodes_by_id[node.id]\n\n this.onNodeRemoved?.(node)\n\n // close panels\n this.canvasAction((c) => c.checkPanels())\n\n this.setDirtyCanvas(true, true)\n // sure? - almost sure is wrong\n this.afterChange()\n this.change()\n\n this.updateExecutionOrder()\n }\n\n /**\n * Returns a node by its id.\n */\n getNodeById(id: NodeId | null | undefined): LGraphNode | null {\n return id != null ? this._nodes_by_id[id] : null\n }\n\n /**\n * Returns a list of nodes that matches a class\n * @param classObject the class itself (not an string)\n * @returns a list with all the nodes of this type\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n findNodesByClass(classObject: Function, result?: LGraphNode[]): LGraphNode[] {\n result = result || []\n result.length = 0\n const { _nodes } = this\n for (const node of _nodes) {\n if (node.constructor === classObject) result.push(node)\n }\n return result\n }\n\n /**\n * Returns a list of nodes that matches a type\n * @param type the name of the node type\n * @returns a list with all the nodes of this type\n */\n findNodesByType(type: string, result: LGraphNode[]): LGraphNode[] {\n const matchType = type.toLowerCase()\n result = result || []\n result.length = 0\n const { _nodes } = this\n for (const node of _nodes) {\n if (node.type?.toLowerCase() == matchType) result.push(node)\n }\n return result\n }\n\n /**\n * Returns the first node that matches a name in its title\n * @param title the name of the node to search\n * @returns the node or null\n */\n findNodeByTitle(title: string): LGraphNode | null {\n const { _nodes } = this\n for (const node of _nodes) {\n if (node.title == title) return node\n }\n return null\n }\n\n /**\n * Returns a list of nodes that matches a name\n * @param title the name of the node to search\n * @returns a list with all the nodes with this name\n */\n findNodesByTitle(title: string): LGraphNode[] {\n const result: LGraphNode[] = []\n const { _nodes } = this\n for (const node of _nodes) {\n if (node.title == title) result.push(node)\n }\n return result\n }\n\n /**\n * Returns the top-most node in this position of the canvas\n * @param x the x coordinate in canvas space\n * @param y the y coordinate in canvas space\n * @param nodeList a list with all the nodes to search from, by default is all the nodes in the graph\n * @returns the node at this position or null\n */\n getNodeOnPos(\n x: number,\n y: number,\n nodeList?: LGraphNode[]\n ): LGraphNode | null {\n const nodes = nodeList || this._nodes\n let i = nodes.length\n while (--i >= 0) {\n const node = nodes[i]\n if (node.isPointInside(x, y)) return node\n }\n return null\n }\n\n /**\n * Returns the top-most group in that position\n * @param x The x coordinate in canvas space\n * @param y The y coordinate in canvas space\n * @returns The group or null\n */\n getGroupOnPos(x: number, y: number): LGraphGroup | undefined {\n // Iterate backwards through groups to find top-most\n for (let i = this._groups.length - 1; i >= 0; i--) {\n const group = this._groups[i]\n if (group.isPointInside(x, y)) {\n return group\n }\n }\n return undefined\n }\n\n /**\n * Returns the top-most group with a titlebar in the provided position.\n * @param x The x coordinate in canvas space\n * @param y The y coordinate in canvas space\n * @returns The group or null\n */\n getGroupTitlebarOnPos(x: number, y: number): LGraphGroup | undefined {\n // Iterate backwards through groups to find top-most\n for (let i = this._groups.length - 1; i >= 0; i--) {\n const group = this._groups[i]\n if (group.isPointInTitlebar(x, y)) {\n return group\n }\n }\n return undefined\n }\n\n /**\n * Finds a reroute a the given graph point\n * @param x X co-ordinate in graph space\n * @param y Y co-ordinate in graph space\n * @returns The first reroute under the given co-ordinates, or undefined\n */\n getRerouteOnPos(\n x: number,\n y: number,\n reroutes?: Iterable<Reroute>\n ): Reroute | undefined {\n for (const reroute of reroutes ?? this.reroutes.values()) {\n if (reroute.containsPoint([x, y])) return reroute\n }\n }\n\n /**\n * Snaps the provided items to a grid.\n *\n * Item positions are rounded to the nearest multiple of {@link LiteGraph.CANVAS_GRID_SIZE}.\n *\n * When {@link LiteGraph.alwaysSnapToGrid} is enabled\n * and the grid size is falsy, a default of 1 is used.\n * @param items The items to be snapped to the grid\n * @todo Currently only snaps nodes.\n */\n snapToGrid(items: Set<Positionable>): void {\n const snapTo = this.getSnapToGridSize()\n if (!snapTo) return\n\n for (const item of getAllNestedItems(items)) {\n if (!item.pinned) item.snapToGrid(snapTo)\n }\n }\n\n /**\n * Finds the size of the grid that items should be snapped to when moved.\n * @returns The size of the grid that items should be snapped to\n */\n getSnapToGridSize(): number {\n // Default to 1 when always snapping\n return LiteGraph.alwaysSnapToGrid\n ? LiteGraph.CANVAS_GRID_SIZE || 1\n : LiteGraph.CANVAS_GRID_SIZE\n }\n\n /**\n * @deprecated Will be removed in 0.9\n * Checks that the node type matches the node type registered,\n * used when replacing a nodetype by a newer version during execution\n * this replaces the ones using the old version with the new version\n */\n checkNodeTypes() {\n const { _nodes } = this\n for (const [i, node] of _nodes.entries()) {\n const ctor = LiteGraph.registered_node_types[node.type]\n if (node.constructor == ctor) continue\n\n console.warn('node being replaced by newer version:', node.type)\n const newnode = LiteGraph.createNode(node.type)\n if (!newnode) continue\n _nodes[i] = newnode\n newnode.configure(node.serialize())\n newnode.graph = this\n this._nodes_by_id[newnode.id] = newnode\n\n if (node.inputs) newnode.inputs = [...node.inputs]\n if (node.outputs) newnode.outputs = [...node.outputs]\n }\n this.updateExecutionOrder()\n }\n\n // ********** GLOBALS *****************\n trigger<A extends LGraphTriggerAction>(\n action: A,\n param: LGraphTriggerParam<A>\n ): void\n trigger(action: string, param: unknown): void\n trigger(action: string, param: unknown) {\n // Convert to discriminated union format for typed handlers\n const validEventTypes = new Set([\n 'node:slot-links:changed',\n 'node:slot-errors:changed',\n 'node:property:changed'\n ])\n\n if (validEventTypes.has(action) && param && typeof param === 'object') {\n this.onTrigger?.({ type: action, ...param } as LGraphTriggerEvent)\n }\n // Don't handle unknown events - just ignore them\n }\n\n /** @todo Clean up - never implemented. */\n triggerInput(name: string, value: unknown): void {\n const nodes = this.findNodesByTitle(name)\n for (const node of nodes) {\n // @ts-expect-error - onTrigger method may not exist on all node types\n node.onTrigger(value)\n }\n }\n\n /** @todo Clean up - never implemented. */\n setCallback(name: string, func?: () => void): void {\n const nodes = this.findNodesByTitle(name)\n for (const node of nodes) {\n // @ts-expect-error - setTrigger method may not exist on all node types\n node.setTrigger(func)\n }\n }\n\n // used for undo, called before any change is made to the graph\n beforeChange(info?: LGraphNode): void {\n this.onBeforeChange?.(this, info)\n this.canvasAction((c) => c.onBeforeChange?.(this))\n }\n\n // used to resend actions, called after any change is made to the graph\n afterChange(info?: LGraphNode | null): void {\n this.onAfterChange?.(this, info)\n this.canvasAction((c) => c.onAfterChange?.(this))\n }\n\n /**\n * clears the triggered slot animation in all links (stop visual animation)\n */\n clearTriggeredSlots(): void {\n for (const link_info of this._links.values()) {\n if (!link_info) continue\n\n if (link_info._last_time) link_info._last_time = 0\n }\n }\n\n /* Called when something visually changed (not the graph!) */\n change(): void {\n this.canvasAction((c) => c.setDirty(true, true))\n this.on_change?.(this)\n }\n\n setDirtyCanvas(fg: boolean, bg?: boolean): void {\n this.canvasAction((c) => c.setDirty(fg, bg))\n }\n\n addFloatingLink(link: LLink): LLink {\n if (link.id === -1) {\n link.id = ++this.#lastFloatingLinkId\n }\n this.floatingLinksInternal.set(link.id, link)\n\n const slot =\n link.target_id !== -1\n ? this.getNodeById(link.target_id)?.inputs?.[link.target_slot]\n : this.getNodeById(link.origin_id)?.outputs?.[link.origin_slot]\n if (slot) {\n slot._floatingLinks ??= new Set()\n slot._floatingLinks.add(link)\n } else {\n console.warn(\n `Adding invalid floating link: target/slot: [${link.target_id}/${link.target_slot}] origin/slot: [${link.origin_id}/${link.origin_slot}]`\n )\n }\n\n const reroutes = LLink.getReroutes(this, link)\n for (const reroute of reroutes) {\n reroute.floatingLinkIds.add(link.id)\n }\n return link\n }\n\n removeFloatingLink(link: LLink): void {\n this.floatingLinksInternal.delete(link.id)\n\n const slot =\n link.target_id !== -1\n ? this.getNodeById(link.target_id)?.inputs?.[link.target_slot]\n : this.getNodeById(link.origin_id)?.outputs?.[link.origin_slot]\n if (slot) {\n slot._floatingLinks?.delete(link)\n }\n\n const reroutes = LLink.getReroutes(this, link)\n for (const reroute of reroutes) {\n reroute.floatingLinkIds.delete(link.id)\n if (reroute.floatingLinkIds.size === 0) {\n delete reroute.floating\n }\n\n if (reroute.totalLinks === 0) this.removeReroute(reroute.id)\n }\n }\n\n /**\n * Finds the link with the provided ID.\n * @param id ID of link to find\n * @returns The link with the provided {@link id}, otherwise `undefined`. Always returns `undefined` if `id` is nullish.\n */\n getLink(id: null | undefined): undefined\n getLink(id: LinkId | null | undefined): LLink | undefined\n getLink(id: LinkId | null | undefined): LLink | undefined {\n return id == null ? undefined : this._links.get(id)\n }\n\n /**\n * Finds the reroute with the provided ID.\n * @param id ID of reroute to find\n * @returns The reroute with the provided {@link id}, otherwise `undefined`. Always returns `undefined` if `id` is nullish.\n */\n getReroute(id: null | undefined): undefined\n getReroute(id: RerouteId | null | undefined): Reroute | undefined\n getReroute(id: RerouteId | null | undefined): Reroute | undefined {\n return id == null ? undefined : this.reroutes.get(id)\n }\n\n /**\n * Configures a reroute on the graph where ID is already known (probably deserialisation).\n * Creates the object if it does not exist.\n * @param serialisedReroute See {@link SerialisableReroute}\n */\n setReroute({\n id,\n parentId,\n pos,\n linkIds,\n floating\n }: OptionalProps<SerialisableReroute, 'id'>): Reroute {\n id ??= ++this.state.lastRerouteId\n if (id > this.state.lastRerouteId) this.state.lastRerouteId = id\n\n const reroute = this.reroutes.get(id) ?? new Reroute(id, this)\n reroute.update(parentId, pos, linkIds, floating)\n this.reroutes.set(id, reroute)\n return reroute\n }\n\n /**\n * Creates a new reroute and adds it to the graph.\n * @param pos Position in graph space\n * @param before The existing link segment (reroute, link) that will be after this reroute,\n * going from the node output to input.\n * @returns The newly created reroute - typically ignored.\n */\n createReroute(pos: Point, before: LinkSegment): Reroute {\n const layoutMutations = useLayoutMutations()\n const rerouteId = ++this.state.lastRerouteId\n const linkIds = before instanceof Reroute ? before.linkIds : [before.id]\n const floatingLinkIds =\n before instanceof Reroute ? before.floatingLinkIds : [before.id]\n const reroute = new Reroute(\n rerouteId,\n this,\n pos,\n before.parentId,\n linkIds,\n floatingLinkIds\n )\n this.reroutes.set(rerouteId, reroute)\n\n // Register reroute in Layout Store for spatial tracking\n layoutMutations.setSource(LayoutSource.Canvas)\n layoutMutations.createReroute(\n rerouteId,\n { x: pos[0], y: pos[1] },\n before.parentId,\n Array.from(linkIds)\n )\n\n for (const linkId of linkIds) {\n const link = this._links.get(linkId)\n if (!link) continue\n if (link.parentId === before.parentId) link.parentId = rerouteId\n\n const reroutes = LLink.getReroutes(this, link)\n for (const x of reroutes.filter((x) => x.parentId === before.parentId)) {\n x.parentId = rerouteId\n }\n }\n\n for (const linkId of floatingLinkIds) {\n const link = this.floatingLinks.get(linkId)\n if (!link) continue\n if (link.parentId === before.parentId) link.parentId = rerouteId\n\n const reroutes = LLink.getReroutes(this, link)\n for (const x of reroutes.filter((x) => x.parentId === before.parentId)) {\n x.parentId = rerouteId\n }\n }\n\n return reroute\n }\n\n /**\n * Removes a reroute from the graph\n * @param id ID of reroute to remove\n */\n removeReroute(id: RerouteId): void {\n const layoutMutations = useLayoutMutations()\n const { reroutes } = this\n const reroute = reroutes.get(id)\n if (!reroute) return\n\n this.canvasAction((c) => c.deselect(reroute))\n\n // Extract reroute from the reroute chain\n const { parentId, linkIds, floatingLinkIds } = reroute\n for (const reroute of reroutes.values()) {\n if (reroute.parentId === id) reroute.parentId = parentId\n }\n\n for (const linkId of linkIds) {\n const link = this._links.get(linkId)\n if (link && link.parentId === id) link.parentId = parentId\n }\n\n for (const linkId of floatingLinkIds) {\n const link = this.floatingLinks.get(linkId)\n if (!link) {\n console.warn(\n `Removed reroute had floating link ID that did not exist [${linkId}]`\n )\n continue\n }\n\n // A floating link is a unique branch; if there is no parent reroute, or\n // the parent reroute has any other links, remove this floating link.\n const floatingReroutes = LLink.getReroutes(this, link)\n const lastReroute = floatingReroutes.at(-1)\n const secondLastReroute = floatingReroutes.at(-2)\n\n if (reroute !== lastReroute) {\n continue\n } else if (secondLastReroute?.totalLinks !== 1) {\n this.removeFloatingLink(link)\n } else if (link.parentId === id) {\n link.parentId = parentId\n secondLastReroute.floating = reroute.floating\n }\n }\n\n reroutes.delete(id)\n\n // Delete reroute from Layout Store\n layoutMutations.setSource(LayoutSource.Canvas)\n layoutMutations.deleteReroute(id)\n\n // This does not belong here; it should be handled by the caller, or run by a remove-many API.\n // https://github.com/Comfy-Org/litegraph.js/issues/898\n this.setDirtyCanvas(false, true)\n }\n\n /**\n * Destroys a link\n */\n removeLink(link_id: LinkId): void {\n const link = this._links.get(link_id)\n if (!link) return\n\n const node = this.getNodeById(link.target_id)\n node?.disconnectInput(link.target_slot, false)\n\n link.disconnect(this)\n }\n\n /**\n * Creates a new subgraph definition, and adds it to the graph.\n * @param data Exported data (typically serialised) to configure the new subgraph with\n * @returns The newly created subgraph definition.\n */\n createSubgraph(data: ExportedSubgraph): Subgraph {\n const { id } = data\n\n const subgraph = new Subgraph(this.rootGraph, data)\n this.subgraphs.set(id, subgraph)\n\n // FE: Create node defs\n this.rootGraph.events.dispatch('subgraph-created', { subgraph, data })\n return subgraph\n }\n\n convertToSubgraph(items: Set<Positionable>): {\n subgraph: Subgraph\n node: SubgraphNode\n } {\n if (items.size === 0)\n throw new Error('Cannot convert to subgraph: nothing to convert')\n\n // Record state before conversion for proper undo support\n this.beforeChange()\n\n try {\n return this._convertToSubgraphImpl(items)\n } finally {\n // Mark state change complete for proper undo support\n this.afterChange()\n }\n }\n\n private _convertToSubgraphImpl(items: Set<Positionable>): {\n subgraph: Subgraph\n node: SubgraphNode\n } {\n const { state, revision, config } = this\n const firstChild = [...items][0]\n if (items.size === 1 && firstChild instanceof LGraphGroup) {\n items = new Set([firstChild])\n firstChild.recomputeInsideNodes()\n firstChild.children.forEach((n) => items.add(n))\n }\n\n const {\n boundaryLinks,\n boundaryFloatingLinks,\n internalLinks,\n boundaryInputLinks,\n boundaryOutputLinks\n } = getBoundaryLinks(this, items)\n const { nodes, reroutes, groups } = splitPositionables(items)\n\n const boundingRect = createBounds(items)\n if (!boundingRect)\n throw new Error('Failed to create bounding rect for subgraph')\n\n const resolvedInputLinks = boundaryInputLinks.map((x) => x.resolve(this))\n const resolvedOutputLinks = boundaryOutputLinks.map((x) => x.resolve(this))\n\n const clonedNodes = multiClone(nodes)\n\n // Inputs, outputs, and links\n const links = internalLinks.map((x) => x.asSerialisable())\n\n const internalReroutes = new Map([...reroutes].map((r) => [r.id, r]))\n const externalReroutes = new Map(\n [...this.reroutes].filter(([id]) => !internalReroutes.has(id))\n )\n const inputs = mapSubgraphInputsAndLinks(\n resolvedInputLinks,\n links,\n internalReroutes\n )\n const outputs = mapSubgraphOutputsAndLinks(\n resolvedOutputLinks,\n links,\n externalReroutes\n )\n\n // Prepare subgraph data\n const data = {\n id: createUuidv4(),\n name: 'New Subgraph',\n inputNode: {\n id: SUBGRAPH_INPUT_ID,\n bounding: [0, 0, 75, 100]\n },\n outputNode: {\n id: SUBGRAPH_OUTPUT_ID,\n bounding: [0, 0, 75, 100]\n },\n inputs,\n outputs,\n widgets: [],\n version: LGraph.serialisedSchemaVersion,\n state,\n revision,\n config,\n links,\n nodes: clonedNodes,\n reroutes: structuredClone(\n [...reroutes].map((reroute) => reroute.asSerialisable())\n ),\n groups: structuredClone([...groups].map((group) => group.serialize()))\n } satisfies ExportedSubgraph\n\n const subgraph = this.createSubgraph(data)\n subgraph.configure(data)\n for (const node of subgraph.nodes) node.onGraphConfigured?.()\n for (const node of subgraph.nodes) node.onAfterGraphConfigured?.()\n\n // Position the subgraph input nodes\n subgraph.inputNode.arrange()\n subgraph.outputNode.arrange()\n const { boundingRect: inputRect } = subgraph.inputNode\n const { boundingRect: outputRect } = subgraph.outputNode\n alignOutsideContainer(inputRect, Alignment.MidLeft, boundingRect, [50, 0])\n alignOutsideContainer(outputRect, Alignment.MidRight, boundingRect, [50, 0])\n\n // Remove items converted to subgraph\n for (const resolved of resolvedInputLinks)\n resolved.inputNode?.disconnectInput(\n resolved.inputNode.inputs.indexOf(resolved.input!),\n true\n )\n for (const resolved of resolvedOutputLinks)\n resolved.outputNode?.disconnectOutput(\n resolved.outputNode.outputs.indexOf(resolved.output!),\n resolved.inputNode\n )\n\n for (const node of nodes) this.remove(node)\n for (const reroute of reroutes) this.removeReroute(reroute.id)\n for (const group of groups) this.remove(group)\n\n this.rootGraph.events.dispatch('convert-to-subgraph', {\n subgraph,\n bounds: boundingRect,\n exportedSubgraph: data,\n boundaryLinks,\n resolvedInputLinks,\n resolvedOutputLinks,\n boundaryFloatingLinks,\n internalLinks\n })\n\n // Create subgraph node object\n const subgraphNode = LiteGraph.createNode(subgraph.id, subgraph.name, {\n outputs: structuredClone(outputs)\n })\n if (!subgraphNode) throw new Error('Failed to create subgraph node')\n for (let i = 0; i < inputs.length; i++) {\n Object.assign(subgraphNode.inputs[i], inputs[i])\n }\n\n // Resize to inputs/outputs\n subgraphNode.setSize(subgraphNode.computeSize())\n\n // Center the subgraph node\n alignToContainer(\n subgraphNode._posSize,\n Alignment.Centre | Alignment.Middle,\n boundingRect\n )\n\n //Correct for title height. It's included in bounding box, but not _posSize\n subgraphNode.pos[1] += LiteGraph.NODE_TITLE_HEIGHT / 2\n\n // Add the subgraph node to the graph\n this.add(subgraphNode)\n\n // Group matching input links\n const groupedByOutput = groupResolvedByOutput(resolvedInputLinks)\n\n // Reconnect input links in parent graph\n let i = 0\n for (const [, connections] of groupedByOutput.entries()) {\n const [firstResolved, ...others] = connections\n const { output, outputNode, link, subgraphInput } = firstResolved\n\n // Special handling: Subgraph input node\n i++\n if (link.origin_id === SUBGRAPH_INPUT_ID) {\n link.target_id = subgraphNode.id\n link.target_slot = i - 1\n if (subgraphInput instanceof SubgraphInput) {\n subgraphInput.connect(\n subgraphNode.findInputSlotByType(link.type, true, true),\n subgraphNode,\n link.parentId\n )\n } else {\n throw new TypeError('Subgraph input node is not a SubgraphInput')\n }\n\n for (const resolved of others) {\n resolved.link.disconnect(this)\n }\n continue\n }\n\n if (!output || !outputNode) {\n console.warn(\n 'Convert to Subgraph reconnect: Failed to resolve input link',\n connections[0]\n )\n continue\n }\n\n const input = subgraphNode.inputs[i - 1]\n outputNode.connectSlots(output, subgraphNode, input, link.parentId)\n }\n\n // Group matching links\n const outputsGroupedByOutput = groupResolvedByOutput(resolvedOutputLinks)\n\n // Reconnect output links in parent graph\n i = 0\n for (const [, connections] of outputsGroupedByOutput.entries()) {\n i++\n for (const connection of connections) {\n const { input, inputNode, link, subgraphOutput } = connection\n // Special handling: Subgraph output node\n if (link.target_id === SUBGRAPH_OUTPUT_ID) {\n link.origin_id = subgraphNode.id\n link.origin_slot = i - 1\n this.links.set(link.id, link)\n if (subgraphOutput instanceof SubgraphOutput) {\n subgraphOutput.connect(\n subgraphNode.findOutputSlotByType(link.type, true, true),\n subgraphNode,\n link.parentId\n )\n } else {\n throw new TypeError('Subgraph input node is not a SubgraphInput')\n }\n continue\n }\n\n if (!input || !inputNode) {\n console.warn(\n 'Convert to Subgraph reconnect: Failed to resolve output link',\n connection\n )\n continue\n }\n\n const output = subgraphNode.outputs[i - 1]\n subgraphNode.connectSlots(output, inputNode, input, link.parentId)\n }\n }\n\n subgraphNode._setConcreteSlots()\n subgraphNode.arrange()\n\n this.canvasAction((c) =>\n c.canvas.dispatchEvent(\n new CustomEvent('subgraph-converted', {\n bubbles: true,\n detail: { subgraphNode: subgraphNode as SubgraphNode }\n })\n )\n )\n return { subgraph, node: subgraphNode as SubgraphNode }\n }\n\n unpackSubgraph(\n subgraphNode: SubgraphNode,\n options?: { skipMissingNodes?: boolean }\n ) {\n if (!(subgraphNode instanceof SubgraphNode))\n throw new Error('Can only unpack Subgraph Nodes')\n\n // Record state before unpacking for proper undo support\n this.beforeChange()\n\n try {\n this._unpackSubgraphImpl(subgraphNode, options)\n } finally {\n // Mark state change complete for proper undo support\n this.afterChange()\n }\n }\n\n private _unpackSubgraphImpl(\n subgraphNode: SubgraphNode,\n options?: { skipMissingNodes?: boolean }\n ) {\n const skipMissingNodes = options?.skipMissingNodes ?? false\n\n //NOTE: Create bounds can not be called on positionables directly as the subgraph is not being displayed and boundingRect is not initialized.\n //NOTE: NODE_TITLE_HEIGHT is explicitly excluded here\n const positionables = [\n ...subgraphNode.subgraph.nodes,\n ...subgraphNode.subgraph.reroutes.values(),\n ...subgraphNode.subgraph.groups\n ].map((p: { pos: Point; size?: Size }): HasBoundingRect => {\n return {\n boundingRect: [p.pos[0], p.pos[1], p.size?.[0] ?? 0, p.size?.[1] ?? 0]\n }\n })\n const bounds = createBounds(positionables) ?? [0, 0, 0, 0]\n const center = [bounds[0] + bounds[2] / 2, bounds[1] + bounds[3] / 2]\n\n const toSelect: Positionable[] = []\n const offsetX = subgraphNode.pos[0] - center[0] + subgraphNode.size[0] / 2\n const offsetY = subgraphNode.pos[1] - center[1] + subgraphNode.size[1] / 2\n const movedNodes = multiClone(subgraphNode.subgraph.nodes)\n const nodeIdMap = new Map<NodeId, NodeId>()\n for (const n_info of movedNodes) {\n let node = LiteGraph.createNode(String(n_info.type), n_info.title)\n if (!node) {\n if (skipMissingNodes) {\n console.warn(\n `Cannot unpack node of type \"${n_info.type}\" - node type not found. Creating placeholder node.`\n )\n node = new LGraphNode(n_info.title || n_info.type || 'Missing Node')\n node.last_serialization = n_info\n node.has_errors = true\n node.type = String(n_info.type)\n } else {\n throw new Error(\n `Cannot unpack: node type \"${n_info.type}\" is not registered`\n )\n }\n }\n\n nodeIdMap.set(n_info.id, ++this.last_node_id)\n node.id = this.last_node_id\n n_info.id = this.last_node_id\n\n this.add(node, true)\n node.configure(n_info)\n node.pos[0] += offsetX\n node.pos[1] += offsetY\n for (const input of node.inputs) {\n input.link = null\n }\n for (const output of node.outputs) {\n output.links = []\n }\n toSelect.push(node)\n }\n const groups = structuredClone(\n [...subgraphNode.subgraph.groups].map((g) => g.serialize())\n )\n for (const g_info of groups) {\n const group = new LGraphGroup(g_info.title, g_info.id)\n this.add(group, true)\n group.configure(g_info)\n group.pos[0] += offsetX\n group.pos[1] += offsetY\n toSelect.push(group)\n }\n //cleanup reoute.linkIds now, but leave link.parentIds dangling\n for (const islot of subgraphNode.inputs) {\n if (!islot.link) continue\n const link = this.links.get(islot.link)\n if (!link) {\n console.warn('Broken link', islot, islot.link)\n continue\n }\n for (const reroute of LLink.getReroutes(this, link)) {\n reroute.linkIds.delete(link.id)\n }\n }\n for (const oslot of subgraphNode.outputs) {\n for (const linkId of oslot.links ?? []) {\n const link = this.links.get(linkId)\n if (!link) {\n console.warn('Broken link', oslot, linkId)\n continue\n }\n for (const reroute of LLink.getReroutes(this, link)) {\n reroute.linkIds.delete(link.id)\n }\n }\n }\n const newLinks: {\n oid: NodeId\n oslot: number\n tid: NodeId\n tslot: number\n id: LinkId\n iparent?: RerouteId\n eparent?: RerouteId\n externalFirst: boolean\n }[] = []\n for (const [, link] of subgraphNode.subgraph._links) {\n let externalParentId: RerouteId | undefined\n if (link.origin_id === SUBGRAPH_INPUT_ID) {\n const outerLinkId = subgraphNode.inputs[link.origin_slot].link\n if (!outerLinkId) {\n console.error('Missing Link ID when unpacking')\n continue\n }\n const outerLink = this.links[outerLinkId]\n link.origin_id = outerLink.origin_id\n link.origin_slot = outerLink.origin_slot\n externalParentId = outerLink.parentId\n } else {\n const origin_id = nodeIdMap.get(link.origin_id)\n if (!origin_id) {\n console.error('Missing Link ID when unpacking')\n continue\n }\n link.origin_id = origin_id\n }\n if (link.target_id === SUBGRAPH_OUTPUT_ID) {\n for (const linkId of subgraphNode.outputs[link.target_slot].links ??\n []) {\n const sublink = this.links[linkId]\n newLinks.push({\n oid: link.origin_id,\n oslot: link.origin_slot,\n tid: sublink.target_id,\n tslot: sublink.target_slot,\n id: link.id,\n iparent: link.parentId,\n eparent: sublink.parentId,\n externalFirst: true\n })\n sublink.parentId = undefined\n }\n continue\n } else {\n const target_id = nodeIdMap.get(link.target_id)\n if (!target_id) {\n console.error('Missing Link ID when unpacking')\n continue\n }\n link.target_id = target_id\n }\n newLinks.push({\n oid: link.origin_id,\n oslot: link.origin_slot,\n tid: link.target_id,\n tslot: link.target_slot,\n id: link.id,\n iparent: link.parentId,\n eparent: externalParentId,\n externalFirst: false\n })\n }\n this.remove(subgraphNode)\n this.subgraphs.delete(subgraphNode.subgraph.id)\n const linkIdMap = new Map<LinkId, LinkId[]>()\n for (const newLink of newLinks) {\n let created: LLink | null | undefined\n if (newLink.oid == SUBGRAPH_INPUT_ID) {\n if (!(this instanceof Subgraph)) {\n console.error('Ignoring link to subgraph outside subgraph')\n continue\n }\n const tnode = this._nodes_by_id[newLink.tid]\n created = this.inputNode.slots[newLink.oslot].connect(\n tnode.inputs[newLink.tslot],\n tnode\n )\n } else if (newLink.tid == SUBGRAPH_OUTPUT_ID) {\n if (!(this instanceof Subgraph)) {\n console.error('Ignoring link to subgraph outside subgraph')\n continue\n }\n const tnode = this._nodes_by_id[newLink.oid]\n created = this.outputNode.slots[newLink.tslot].connect(\n tnode.outputs[newLink.oslot],\n tnode\n )\n } else {\n created = this._nodes_by_id[newLink.oid].connect(\n newLink.oslot,\n this._nodes_by_id[newLink.tid],\n newLink.tslot\n )\n }\n if (!created) {\n console.error('Failed to create link')\n continue\n }\n //This is a little unwieldy since Map.has isn't a type guard\n const linkIds = linkIdMap.get(newLink.id) ?? []\n linkIds.push(created.id)\n if (!linkIdMap.has(newLink.id)) {\n linkIdMap.set(newLink.id, linkIds)\n }\n newLink.id = created.id\n }\n const rerouteIdMap = new Map<RerouteId, RerouteId>()\n for (const reroute of subgraphNode.subgraph.reroutes.values()) {\n if (\n reroute.parentId !== undefined &&\n rerouteIdMap.get(reroute.parentId) === undefined\n ) {\n console.error('Missing Parent ID')\n }\n const migratedReroute = new Reroute(++this.state.lastRerouteId, this, [\n reroute.pos[0] + offsetX,\n reroute.pos[1] + offsetY\n ])\n rerouteIdMap.set(reroute.id, migratedReroute.id)\n this.reroutes.set(migratedReroute.id, migratedReroute)\n toSelect.push(migratedReroute)\n }\n //iterate over newly created links to update reroute parentIds\n for (const newLink of newLinks) {\n const linkInstance = this.links.get(newLink.id)\n if (!linkInstance) {\n continue\n }\n let instance: Reroute | LLink | undefined = linkInstance\n let parentId: RerouteId | undefined = undefined\n if (newLink.externalFirst) {\n parentId = newLink.eparent\n //TODO: recursion check/helper method? Probably exists, but wouldn't mesh with the reference tracking used by this implementation\n while (parentId) {\n instance.parentId = parentId\n instance = this.reroutes.get(parentId)\n if (!instance) {\n console.error('Broken Id link when unpacking')\n break\n }\n if (instance.linkIds.has(linkInstance.id))\n throw new Error('Infinite parentId loop')\n instance.linkIds.add(linkInstance.id)\n parentId = instance.parentId\n }\n }\n if (!instance) continue\n parentId = newLink.iparent\n while (parentId) {\n const migratedId = rerouteIdMap.get(parentId)\n if (!migratedId) {\n console.error('Broken Id link when unpacking')\n break\n }\n instance.parentId = migratedId\n instance = this.reroutes.get(migratedId)\n if (!instance) {\n console.error('Broken Id link when unpacking')\n break\n }\n if (instance.linkIds.has(linkInstance.id))\n throw new Error('Infinite parentId loop')\n instance.linkIds.add(linkInstance.id)\n const oldReroute = subgraphNode.subgraph.reroutes.get(parentId)\n if (!oldReroute) {\n console.error('Broken Id link when unpacking')\n break\n }\n parentId = oldReroute.parentId\n }\n if (!instance) break\n if (!newLink.externalFirst) {\n parentId = newLink.eparent\n while (parentId) {\n instance.parentId = parentId\n instance = this.reroutes.get(parentId)\n if (!instance) {\n console.error('Broken Id link when unpacking')\n break\n }\n if (instance.linkIds.has(linkInstance.id))\n throw new Error('Infinite parentId loop')\n instance.linkIds.add(linkInstance.id)\n parentId = instance.parentId\n }\n }\n }\n\n for (const nodeId of nodeIdMap.values()) {\n const node = this._nodes_by_id[nodeId]\n node._setConcreteSlots()\n node.arrange()\n }\n\n this.canvasAction((c) => c.selectItems(toSelect))\n }\n\n /**\n * Resolve a path of subgraph node IDs into a list of subgraph nodes.\n * Not intended to be run from subgraphs.\n * @param nodeIds An ordered list of node IDs, from the root graph to the most nested subgraph node\n * @returns An ordered list of nested subgraph nodes.\n */\n resolveSubgraphIdPath(nodeIds: readonly NodeId[]): SubgraphNode[] {\n const result: SubgraphNode[] = []\n let currentGraph: GraphOrSubgraph = this.rootGraph\n\n for (const nodeId of nodeIds) {\n const node: LGraphNode | null = currentGraph.getNodeById(nodeId)\n if (!node)\n throw new Error(\n `Node [${nodeId}] not found. ID Path: ${nodeIds.join(':')}`\n )\n if (!node.isSubgraphNode())\n throw new Error(\n `Node [${nodeId}] is not a SubgraphNode. ID Path: ${nodeIds.join(':')}`\n )\n\n result.push(node)\n currentGraph = node.subgraph\n }\n\n return result\n }\n\n /**\n * Creates a Object containing all the info about this graph, it can be serialized\n * @deprecated Use {@link asSerialisable}, which returns the newer schema version.\n * @returns value of the node\n */\n serialize(option?: { sortNodes: boolean }): ISerialisedGraph {\n const {\n config,\n state,\n groups,\n nodes,\n reroutes,\n extra,\n floatingLinks,\n definitions\n } = this.asSerialisable(option)\n const linkArray = [...this._links.values()]\n const links = linkArray.map((x) => x.serialize())\n\n if (reroutes?.length) {\n // Link parent IDs cannot go in 0.4 schema arrays\n extra.linkExtensions = linkArray\n .filter((x) => x.parentId !== undefined)\n .map((x) => ({ id: x.id, parentId: x.parentId }))\n }\n\n extra.reroutes = reroutes?.length ? reroutes : undefined\n return {\n id: this.id,\n revision: this.revision,\n last_node_id: state.lastNodeId,\n last_link_id: state.lastLinkId,\n nodes,\n links,\n floatingLinks,\n groups,\n definitions,\n config,\n extra,\n version: LiteGraph.VERSION\n }\n }\n\n /** @returns The drag and scale state of the first attached canvas, otherwise `undefined`. */\n #getDragAndScale(): DragAndScaleState | undefined {\n const ds = this.list_of_graphcanvas?.at(0)?.ds\n if (ds) return { scale: ds.scale, offset: ds.offset }\n }\n\n /**\n * Prepares a shallow copy of this object for immediate serialisation or structuredCloning.\n * The return value should be discarded immediately.\n * @param options Serialise options = currently `sortNodes: boolean`, whether to sort nodes by ID.\n * @returns A shallow copy of parts of this graph, with shallow copies of its serialisable objects.\n * Mutating the properties of the return object may result in changes to your graph.\n * It is intended for use with {@link structuredClone} or {@link JSON.stringify}.\n */\n asSerialisable(options?: {\n sortNodes: boolean\n }): SerialisableGraph &\n Required<Pick<SerialisableGraph, 'nodes' | 'groups' | 'extra'>> {\n const { id, revision, config, state } = this\n\n const nodeList =\n !LiteGraph.use_uuids && options?.sortNodes\n ? // @ts-expect-error If LiteGraph.use_uuids is false, ids are numbers.\n [...this._nodes].sort((a, b) => a.id - b.id)\n : this._nodes\n\n const nodes = nodeList.map((node) => node.serialize())\n const groups = this._groups.map((x) => x.serialize())\n\n const links = this._links.size\n ? [...this._links.values()].map((x) => x.asSerialisable())\n : undefined\n const floatingLinks = this.floatingLinks.size\n ? [...this.floatingLinks.values()].map((x) => x.asSerialisable())\n : undefined\n const reroutes = this.reroutes.size\n ? [...this.reroutes.values()].map((x) => x.asSerialisable())\n : undefined\n\n // Save scale and offset\n const extra = { ...this.extra }\n if (LiteGraph.saveViewportWithGraph) extra.ds = this.#getDragAndScale()\n if (!extra.ds) delete extra.ds\n\n const data: ReturnType<typeof this.asSerialisable> = {\n id,\n revision,\n version: LGraph.serialisedSchemaVersion,\n config,\n state,\n groups,\n nodes,\n links,\n floatingLinks,\n reroutes,\n extra\n }\n\n if (this.isRootGraph && this._subgraphs.size) {\n const usedSubgraphIds = findUsedSubgraphIds(this, this._subgraphs)\n const usedSubgraphs = [...this._subgraphs.values()]\n .filter((subgraph) => usedSubgraphIds.has(subgraph.id))\n .map((x) => x.asSerialisable())\n\n if (usedSubgraphs.length > 0) {\n data.definitions = { subgraphs: usedSubgraphs }\n }\n }\n\n this.onSerialize?.(data)\n return data\n }\n\n protected _configureBase(data: ISerialisedGraph | SerialisableGraph): void {\n const { id, extra } = data\n\n // Create a new graph ID if none is provided\n if (id) {\n this.id = id\n } else if (this.id === zeroUuid) {\n this.id = createUuidv4()\n }\n\n // Extra\n this.extra = extra ? structuredClone(extra) : {}\n\n // Ensure auto-generated serialisation data is removed from extra\n delete this.extra.linkExtensions\n }\n\n /**\n * Configure a graph from a JSON string\n * @param data The deserialised object to configure this graph from\n * @param keep_old If `true`, the graph will not be cleared prior to\n * adding the configuration.\n */\n configure(\n data: ISerialisedGraph | SerialisableGraph,\n keep_old?: boolean\n ): boolean | undefined {\n const layoutMutations = useLayoutMutations()\n const options: LGraphEventMap['configuring'] = {\n data,\n clearGraph: !keep_old\n }\n const mayContinue = this.events.dispatch('configuring', options)\n if (!mayContinue) return\n\n try {\n // TODO: Finish typing configure()\n if (!data) return\n if (options.clearGraph) this.clear()\n\n this._configureBase(data)\n\n let reroutes: SerialisableReroute[] | undefined\n\n // TODO: Determine whether this should this fall back to 0.4.\n if (data.version === 0.4) {\n const { extra } = data\n // Deprecated - old schema version, links are arrays\n if (Array.isArray(data.links)) {\n for (const linkData of data.links) {\n const link = LLink.createFromArray(linkData)\n this._links.set(link.id, link)\n }\n }\n // #region `extra` embeds for v0.4\n\n // LLink parentIds\n if (Array.isArray(extra?.linkExtensions)) {\n for (const linkEx of extra.linkExtensions) {\n const link = this._links.get(linkEx.id)\n if (link) link.parentId = linkEx.parentId\n }\n }\n\n // Reroutes\n reroutes = extra?.reroutes\n\n // #endregion `extra` embeds for v0.4\n } else {\n // New schema - one version so far, no check required.\n\n // State\n if (data.state) {\n const { lastGroupId, lastLinkId, lastNodeId, lastRerouteId } =\n data.state\n const { state } = this\n if (lastGroupId != null) state.lastGroupId = lastGroupId\n if (lastLinkId != null) state.lastLinkId = lastLinkId\n if (lastNodeId != null) state.lastNodeId = lastNodeId\n if (lastRerouteId != null) state.lastRerouteId = lastRerouteId\n }\n\n // Links\n if (Array.isArray(data.links)) {\n for (const linkData of data.links) {\n const link = LLink.create(linkData)\n this._links.set(link.id, link)\n }\n }\n\n reroutes = data.reroutes\n }\n\n // Reroutes\n if (Array.isArray(reroutes)) {\n for (const rerouteData of reroutes) {\n this.setReroute(rerouteData)\n }\n }\n\n const nodesData = data.nodes\n\n // copy all stored fields\n for (const i in data) {\n if (LGraph.ConfigureProperties.has(i)) continue\n\n // @ts-expect-error #574 Legacy property assignment\n this[i] = data[i]\n }\n\n // Subgraph definitions\n const subgraphs = data.definitions?.subgraphs\n if (subgraphs) {\n for (const subgraph of subgraphs) this.createSubgraph(subgraph)\n for (const subgraph of subgraphs)\n this.subgraphs.get(subgraph.id)?.configure(subgraph)\n }\n\n let error = false\n const nodeDataMap = new Map<NodeId, ISerialisedNode>()\n\n // create nodes\n this._nodes = []\n if (nodesData) {\n for (const n_info of nodesData) {\n // stored info\n let node = LiteGraph.createNode(String(n_info.type), n_info.title)\n if (!node) {\n if (LiteGraph.debug)\n console.warn('Node not found or has errors:', n_info.type)\n\n // in case of error we create a replacement node to avoid losing info\n node = new LGraphNode('')\n node.last_serialization = n_info\n node.has_errors = true\n error = true\n // continue;\n }\n\n // id it or it will create a new id\n node.id = n_info.id\n // add before configure, otherwise configure cannot create links\n this.add(node, true)\n nodeDataMap.set(node.id, n_info)\n }\n\n // configure nodes afterwards so they can reach each other\n for (const [id, nodeData] of nodeDataMap) {\n this.getNodeById(id)?.configure(nodeData)\n }\n }\n\n // Floating links\n if (Array.isArray(data.floatingLinks)) {\n for (const linkData of data.floatingLinks) {\n const floatingLink = LLink.create(linkData)\n this.addFloatingLink(floatingLink)\n\n if (floatingLink.id > this.#lastFloatingLinkId)\n this.#lastFloatingLinkId = floatingLink.id\n }\n }\n\n // Drop broken reroutes\n for (const reroute of this.reroutes.values()) {\n // Drop broken links, and ignore reroutes with no valid links\n if (!reroute.validateLinks(this._links, this.floatingLinks)) {\n this.reroutes.delete(reroute.id)\n // Clean up layout store\n layoutMutations.setSource(LayoutSource.Canvas)\n layoutMutations.deleteReroute(reroute.id)\n }\n }\n\n // groups\n this._groups.length = 0\n const groupData = data.groups\n if (groupData) {\n for (const data of groupData) {\n // TODO: Search/remove these global object refs\n const group = new LiteGraph.LGraphGroup()\n group.configure(data)\n this.add(group)\n }\n }\n\n this.updateExecutionOrder()\n\n this.onConfigure?.(data)\n this._version++\n\n // Ensure the primary canvas is set to the correct graph\n const { primaryCanvas } = this\n const subgraphId = primaryCanvas?.subgraph?.id\n if (subgraphId) {\n const subgraph = this.subgraphs.get(subgraphId)\n if (subgraph) {\n primaryCanvas.setGraph(subgraph)\n } else {\n primaryCanvas.setGraph(this)\n }\n }\n\n this.setDirtyCanvas(true, true)\n return error\n } finally {\n this.events.dispatch('configured')\n }\n }\n\n #canvas?: LGraphCanvas\n get primaryCanvas(): LGraphCanvas | undefined {\n return this.rootGraph.#canvas\n }\n\n set primaryCanvas(canvas: LGraphCanvas) {\n this.rootGraph.#canvas = canvas\n }\n\n load(url: string | Blob | URL | File, callback: () => void) {\n // from file\n if (url instanceof Blob || url instanceof File) {\n const reader = new FileReader()\n reader.addEventListener('load', (event) => {\n const result = toString(event.target?.result)\n const data = JSON.parse(result)\n this.configure(data)\n callback?.()\n })\n\n reader.readAsText(url)\n return\n }\n\n // is a string, then an URL\n const req = new XMLHttpRequest()\n req.open('GET', url, true)\n req.send(null)\n req.addEventListener('load', () => {\n if (req.status !== 200) {\n console.error('Error loading graph:', req.status, req.response)\n return\n }\n const data = JSON.parse(req.response)\n this.configure(data)\n callback?.()\n })\n req.addEventListener('error', (err) => {\n console.error('Error loading graph:', err)\n })\n }\n}\n\n/** Internal; simplifies type definitions. */\nexport type GraphOrSubgraph = LGraph | Subgraph\n\n// ============================================================================\n// TEMPORARY: Subgraph class moved here to resolve circular dependency\n// This is a temporary solution until the architecture can be refactored\n// TODO: Move back to separate file once circular dependencies are resolved\n// ============================================================================\n\n/** A subgraph definition. */\nexport class Subgraph\n extends LGraph\n implements BaseLGraph, Serialisable<ExportedSubgraph>\n{\n override readonly events = new CustomEventTarget<SubgraphEventMap>()\n\n /** Limits the number of levels / depth that subgraphs may be nested. Prevents uncontrolled programmatic nesting. */\n static MAX_NESTED_SUBGRAPHS = 1000\n\n /** The display name of the subgraph. */\n name: string = 'Unnamed Subgraph'\n\n readonly inputNode = new SubgraphInputNode(this)\n readonly outputNode = new SubgraphOutputNode(this)\n\n /** Ordered list of inputs to the subgraph itself. Similar to a reroute, with the input side in the graph, and the output side in the subgraph. */\n readonly inputs: SubgraphInput[] = []\n /** Ordered list of outputs from the subgraph itself. Similar to a reroute, with the input side in the subgraph, and the output side in the graph. */\n readonly outputs: SubgraphOutput[] = []\n /** A list of node widgets displayed in the parent graph, on the subgraph object. */\n readonly widgets: ExposedWidget[] = []\n\n #rootGraph: LGraph\n override get rootGraph(): LGraph {\n return this.#rootGraph\n }\n\n constructor(rootGraph: LGraph, data: ExportedSubgraph) {\n if (!rootGraph) throw new Error('Root graph is required')\n\n super()\n\n this.#rootGraph = rootGraph\n\n const cloned = structuredClone(data)\n this._configureBase(cloned)\n this.#configureSubgraph(cloned)\n }\n\n getIoNodeOnPos(\n x: number,\n y: number\n ): SubgraphInputNode | SubgraphOutputNode | undefined {\n const { inputNode, outputNode } = this\n if (inputNode.containsPoint([x, y])) return inputNode\n if (outputNode.containsPoint([x, y])) return outputNode\n }\n\n #configureSubgraph(\n data:\n | (ISerialisedGraph & ExportedSubgraph)\n | (SerialisableGraph & ExportedSubgraph)\n ): void {\n const { name, inputs, outputs, widgets } = data\n\n this.name = name\n if (inputs) {\n this.inputs.length = 0\n for (const input of inputs) {\n const subgraphInput = new SubgraphInput(input, this.inputNode)\n this.inputs.push(subgraphInput)\n this.events.dispatch('input-added', { input: subgraphInput })\n }\n }\n\n if (outputs) {\n this.outputs.length = 0\n for (const output of outputs) {\n this.outputs.push(new SubgraphOutput(output, this.outputNode))\n }\n }\n\n if (widgets) {\n this.widgets.length = 0\n for (const widget of widgets) {\n this.widgets.push(widget)\n }\n }\n\n this.inputNode.configure(data.inputNode)\n this.outputNode.configure(data.outputNode)\n }\n\n override configure(\n data:\n | (ISerialisedGraph & ExportedSubgraph)\n | (SerialisableGraph & ExportedSubgraph),\n keep_old?: boolean\n ): boolean | undefined {\n const r = super.configure(data, keep_old)\n\n this.#configureSubgraph(data)\n return r\n }\n\n override attachCanvas(canvas: LGraphCanvas): void {\n super.attachCanvas(canvas)\n canvas.subgraph = this\n }\n\n addInput(name: string, type: string): SubgraphInput {\n this.events.dispatch('adding-input', { name, type })\n\n const input = new SubgraphInput(\n {\n id: createUuidv4(),\n name,\n type\n },\n this.inputNode\n )\n\n this.inputs.push(input)\n this.events.dispatch('input-added', { input })\n\n return input\n }\n\n addOutput(name: string, type: string): SubgraphOutput {\n this.events.dispatch('adding-output', { name, type })\n\n const output = new SubgraphOutput(\n {\n id: createUuidv4(),\n name,\n type\n },\n this.outputNode\n )\n\n this.outputs.push(output)\n this.events.dispatch('output-added', { output })\n\n return output\n }\n\n /**\n * Renames an input slot in the subgraph.\n * @param input The input slot to rename.\n * @param name The new name for the input slot.\n */\n renameInput(input: SubgraphInput, name: string): void {\n const index = this.inputs.indexOf(input)\n if (index === -1) throw new Error('Input not found')\n\n const oldName = input.displayName\n this.events.dispatch('renaming-input', {\n input,\n index,\n oldName,\n newName: name\n })\n\n input.label = name\n }\n\n /**\n * Renames an output slot in the subgraph.\n * @param output The output slot to rename.\n * @param name The new name for the output slot.\n */\n renameOutput(output: SubgraphOutput, name: string): void {\n const index = this.outputs.indexOf(output)\n if (index === -1) throw new Error('Output not found')\n\n const oldName = output.displayName\n this.events.dispatch('renaming-output', {\n output,\n index,\n oldName,\n newName: name\n })\n\n output.label = name\n }\n\n /**\n * Removes an input slot from the subgraph.\n * @param input The input slot to remove.\n */\n removeInput(input: SubgraphInput): void {\n input.disconnect()\n\n const index = this.inputs.indexOf(input)\n if (index === -1) throw new Error('Input not found')\n\n const mayContinue = this.events.dispatch('removing-input', { input, index })\n if (!mayContinue) return\n\n this.inputs.splice(index, 1)\n\n const { length } = this.inputs\n for (let i = index; i < length; i++) {\n this.inputs[i].decrementSlots('inputs')\n }\n }\n\n /**\n * Removes an output slot from the subgraph.\n * @param output The output slot to remove.\n */\n removeOutput(output: SubgraphOutput): void {\n output.disconnect()\n\n const index = this.outputs.indexOf(output)\n if (index === -1) throw new Error('Output not found')\n\n const mayContinue = this.events.dispatch('removing-output', {\n output,\n index\n })\n if (!mayContinue) return\n\n this.outputs.splice(index, 1)\n\n const { length } = this.outputs\n for (let i = index; i < length; i++) {\n this.outputs[i].decrementSlots('outputs')\n }\n }\n\n draw(\n ctx: CanvasRenderingContext2D,\n colorContext: DefaultConnectionColors,\n fromSlot?:\n | INodeInputSlot\n | INodeOutputSlot\n | SubgraphInput\n | SubgraphOutput,\n editorAlpha?: number\n ): void {\n this.inputNode.draw(ctx, colorContext, fromSlot, editorAlpha)\n this.outputNode.draw(ctx, colorContext, fromSlot, editorAlpha)\n }\n\n /**\n * Clones the subgraph, creating an identical copy with a new ID.\n * @returns A new subgraph with the same configuration, but a new ID.\n */\n clone(keepId: boolean = false): Subgraph {\n const exported = this.asSerialisable()\n if (!keepId) exported.id = createUuidv4()\n\n const subgraph = new Subgraph(this.rootGraph, exported)\n subgraph.configure(exported)\n return subgraph\n }\n\n override asSerialisable(): ExportedSubgraph &\n Required<Pick<SerialisableGraph, 'nodes' | 'groups' | 'extra'>> {\n return {\n id: this.id,\n version: LGraph.serialisedSchemaVersion,\n state: this.state,\n revision: this.revision,\n config: this.config,\n name: this.name,\n inputNode: this.inputNode.asSerialisable(),\n outputNode: this.outputNode.asSerialisable(),\n inputs: this.inputs.map((x) => x.asSerialisable()),\n outputs: this.outputs.map((x) => x.asSerialisable()),\n widgets: [...this.widgets],\n nodes: this.nodes.map((node) => node.serialize()),\n groups: this.groups.map((group) => group.serialize()),\n links: [...this.links.values()].map((x) => x.asSerialisable()),\n reroutes: this.reroutes.size\n ? [...this.reroutes.values()].map((x) => x.asSerialisable())\n : undefined,\n extra: this.extra\n }\n }\n}\n","import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'\n\n/**\n * A class that can be added to the render cycle to show pointer / keyboard status symbols.\n *\n * Used to create videos of feature changes.\n *\n * Example usage with ComfyUI_frontend, via console / devtools:\n *\n * ```ts\n * const inputIndicators = new InputIndicators(canvas)\n * // Dispose:\n * inputIndicators.dispose()\n * ```\n */\nexport class InputIndicators implements Disposable {\n // #region config\n radius = 8\n startAngle = 0\n endAngle = Math.PI * 2\n\n inactiveColour = '#ffffff10'\n colour1 = '#ff5f00'\n colour2 = '#00ff7c'\n colour3 = '#dea7ff'\n fontString = 'bold 12px Inter, sans-serif'\n // #endregion\n\n // #region state\n enabled: boolean = true\n\n shiftDown: boolean = false\n undoDown: boolean = false\n redoDown: boolean = false\n ctrlDown: boolean = false\n altDown: boolean = false\n mouse0Down: boolean = false\n mouse1Down: boolean = false\n mouse2Down: boolean = false\n\n x: number = 0\n y: number = 0\n // #endregion\n\n controller?: AbortController\n\n constructor(public canvas: LGraphCanvas) {\n this.controller = new AbortController()\n const { signal } = this.controller\n\n const element = canvas.canvas\n const options = { capture: true, signal } satisfies AddEventListenerOptions\n\n element.addEventListener('pointerdown', this.#onPointerDownOrMove, options)\n element.addEventListener('pointermove', this.#onPointerDownOrMove, options)\n element.addEventListener('pointerup', this.#onPointerUp, options)\n element.addEventListener('keydown', this.#onKeyDownOrUp, options)\n document.addEventListener('keyup', this.#onKeyDownOrUp, options)\n\n const origDrawFrontCanvas = canvas.drawFrontCanvas.bind(canvas)\n signal.addEventListener('abort', () => {\n canvas.drawFrontCanvas = origDrawFrontCanvas\n })\n\n canvas.drawFrontCanvas = () => {\n origDrawFrontCanvas()\n this.draw()\n }\n }\n\n #onPointerDownOrMove = this.onPointerDownOrMove.bind(this)\n onPointerDownOrMove(e: MouseEvent): void {\n this.mouse0Down = (e.buttons & 1) === 1\n this.mouse1Down = (e.buttons & 4) === 4\n this.mouse2Down = (e.buttons & 2) === 2\n\n this.x = e.clientX\n this.y = e.clientY\n\n this.canvas.setDirty(true)\n }\n\n #onPointerUp = this.onPointerUp.bind(this)\n onPointerUp(): void {\n this.mouse0Down = false\n this.mouse1Down = false\n this.mouse2Down = false\n }\n\n #onKeyDownOrUp = this.onKeyDownOrUp.bind(this)\n onKeyDownOrUp(e: KeyboardEvent): void {\n this.ctrlDown = e.ctrlKey\n this.altDown = e.altKey\n this.shiftDown = e.shiftKey\n this.undoDown = e.ctrlKey && e.code === 'KeyZ' && e.type === 'keydown'\n this.redoDown = e.ctrlKey && e.code === 'KeyY' && e.type === 'keydown'\n }\n\n draw() {\n const {\n canvas: { ctx },\n radius,\n startAngle,\n endAngle,\n x,\n y,\n inactiveColour,\n colour1,\n colour2,\n colour3,\n fontString\n } = this\n\n const { fillStyle, font } = ctx\n\n const mouseDotX = x\n const mouseDotY = y - 80\n\n const textX = mouseDotX\n const textY = mouseDotY - 15\n ctx.font = fontString\n\n textMarker(\n textX + 0,\n textY,\n 'Shift',\n this.shiftDown ? colour1 : inactiveColour\n )\n textMarker(\n textX + 45,\n textY + 20,\n 'Alt',\n this.altDown ? colour2 : inactiveColour\n )\n textMarker(\n textX + 30,\n textY,\n 'Control',\n this.ctrlDown ? colour3 : inactiveColour\n )\n textMarker(textX - 30, textY, '↩️', this.undoDown ? '#000' : 'transparent')\n textMarker(textX + 45, textY, '↪️', this.redoDown ? '#000' : 'transparent')\n\n ctx.beginPath()\n drawDot(mouseDotX, mouseDotY)\n drawDot(mouseDotX + 15, mouseDotY)\n drawDot(mouseDotX + 30, mouseDotY)\n ctx.fillStyle = inactiveColour\n ctx.fill()\n\n const leftButtonColour = this.mouse0Down ? colour1 : inactiveColour\n const middleButtonColour = this.mouse1Down ? colour2 : inactiveColour\n const rightButtonColour = this.mouse2Down ? colour3 : inactiveColour\n if (this.mouse0Down) mouseMarker(mouseDotX, mouseDotY, leftButtonColour)\n if (this.mouse1Down)\n mouseMarker(mouseDotX + 15, mouseDotY, middleButtonColour)\n if (this.mouse2Down)\n mouseMarker(mouseDotX + 30, mouseDotY, rightButtonColour)\n\n ctx.fillStyle = fillStyle\n ctx.font = font\n\n function textMarker(x: number, y: number, text: string, colour: string) {\n ctx.fillStyle = colour\n ctx.fillText(text, x, y)\n }\n\n function mouseMarker(x: number, y: number, colour: string) {\n ctx.beginPath()\n ctx.fillStyle = colour\n drawDot(x, y)\n ctx.fill()\n }\n\n function drawDot(x: number, y: number) {\n ctx.arc(x, y, radius, startAngle, endAngle)\n }\n }\n\n dispose() {\n this.controller?.abort()\n this.controller = undefined\n }\n\n [Symbol.dispose](): void {\n this.dispose()\n }\n}\n","import { ContextMenu } from './ContextMenu'\nimport { CurveEditor } from './CurveEditor'\nimport { DragAndScale } from './DragAndScale'\nimport { LGraph } from './LGraph'\nimport { LGraphCanvas } from './LGraphCanvas'\nimport { LGraphGroup } from './LGraphGroup'\nimport { LGraphNode } from './LGraphNode'\nimport { LLink } from './LLink'\nimport { Reroute } from './Reroute'\nimport { InputIndicators } from './canvas/InputIndicators'\nimport { LabelPosition, SlotDirection, SlotShape, SlotType } from './draw'\nimport { Rectangle } from './infrastructure/Rectangle'\nimport type { Dictionary, ISlotType, Rect, WhenNullish } from './interfaces'\nimport { distance, isInsideRectangle, overlapBounding } from './measure'\nimport { SubgraphIONodeBase } from './subgraph/SubgraphIONodeBase'\nimport { SubgraphSlot } from './subgraph/SubgraphSlotBase'\nimport {\n LGraphEventMode,\n LinkDirection,\n LinkRenderType,\n NodeSlotType,\n RenderShape,\n TitleMode\n} from './types/globalEnums'\nimport { createUuidv4 } from './utils/uuid'\n\n/**\n * The Global Scope. It contains all the registered node classes.\n */\nexport class LiteGraphGlobal {\n // Enums\n SlotShape = SlotShape\n SlotDirection = SlotDirection\n SlotType = SlotType\n LabelPosition = LabelPosition\n\n /** Used in serialised graphs at one point. */\n VERSION = 0.4 as const\n\n CANVAS_GRID_SIZE = 10\n\n NODE_TITLE_HEIGHT = 30\n NODE_TITLE_TEXT_Y = 20\n NODE_SLOT_HEIGHT = 20\n NODE_WIDGET_HEIGHT = 20\n NODE_WIDTH = 140\n NODE_MIN_WIDTH = 50\n NODE_COLLAPSED_RADIUS = 10\n NODE_COLLAPSED_WIDTH = 80\n NODE_TITLE_COLOR = '#999'\n NODE_SELECTED_TITLE_COLOR = '#FFF'\n NODE_TEXT_SIZE = 14\n NODE_TEXT_COLOR = '#AAA'\n NODE_TEXT_HIGHLIGHT_COLOR = '#EEE'\n NODE_SUBTEXT_SIZE = 12\n NODE_DEFAULT_COLOR = '#333'\n NODE_DEFAULT_BGCOLOR = '#353535'\n NODE_DEFAULT_BOXCOLOR = '#666'\n NODE_DEFAULT_SHAPE = RenderShape.ROUND\n NODE_BOX_OUTLINE_COLOR = '#FFF'\n NODE_ERROR_COLOUR = '#E00'\n NODE_FONT = 'Inter'\n NODE_DEFAULT_BYPASS_COLOR = '#FF00FF'\n NODE_OPACITY = 0.9\n\n DEFAULT_FONT = 'Inter'\n DEFAULT_SHADOW_COLOR = 'rgba(0,0,0,0.5)'\n\n DEFAULT_GROUP_FONT = 24\n DEFAULT_GROUP_FONT_SIZE = 24\n GROUP_FONT = 'Inter'\n\n WIDGET_BGCOLOR = '#222'\n WIDGET_OUTLINE_COLOR = '#666'\n WIDGET_PROMOTED_OUTLINE_COLOR = '#BF00FF'\n WIDGET_ADVANCED_OUTLINE_COLOR = 'rgba(56, 139, 253, 0.8)'\n WIDGET_TEXT_COLOR = '#DDD'\n WIDGET_SECONDARY_TEXT_COLOR = '#999'\n WIDGET_DISABLED_TEXT_COLOR = '#666'\n\n LINK_COLOR = '#9A9'\n EVENT_LINK_COLOR = '#A86'\n CONNECTING_LINK_COLOR = '#AFA'\n\n /** avoid infinite loops */\n MAX_NUMBER_OF_NODES = 10_000\n /** default node position */\n DEFAULT_POSITION = [100, 100]\n /** ,\"circle\" */\n VALID_SHAPES = ['default', 'box', 'round', 'card'] satisfies (\n | 'default'\n | Lowercase<keyof typeof RenderShape>\n )[]\n ROUND_RADIUS = 8\n\n // shapes are used for nodes but also for slots\n BOX_SHAPE = RenderShape.BOX\n ROUND_SHAPE = RenderShape.ROUND\n CIRCLE_SHAPE = RenderShape.CIRCLE\n CARD_SHAPE = RenderShape.CARD\n ARROW_SHAPE = RenderShape.ARROW\n /** intended for slot arrays */\n GRID_SHAPE = RenderShape.GRID\n\n // enums\n INPUT = NodeSlotType.INPUT\n OUTPUT = NodeSlotType.OUTPUT\n\n // TODO: -1 can lead to ambiguity in JS; these should be updated to a more explicit constant or Symbol.\n /** for outputs */\n EVENT = -1 as const\n /** for inputs */\n ACTION = -1 as const\n\n /** helper, will add \"On Request\" and more in the future */\n NODE_MODES = ['Always', 'On Event', 'Never', 'On Trigger']\n /** use with node_box_coloured_by_mode */\n NODE_MODES_COLORS = ['#666', '#422', '#333', '#224', '#626']\n ALWAYS = LGraphEventMode.ALWAYS\n ON_EVENT = LGraphEventMode.ON_EVENT\n NEVER = LGraphEventMode.NEVER\n ON_TRIGGER = LGraphEventMode.ON_TRIGGER\n\n UP = LinkDirection.UP\n DOWN = LinkDirection.DOWN\n LEFT = LinkDirection.LEFT\n RIGHT = LinkDirection.RIGHT\n CENTER = LinkDirection.CENTER\n\n /** helper */\n LINK_RENDER_MODES = ['Straight', 'Linear', 'Spline']\n HIDDEN_LINK = LinkRenderType.HIDDEN_LINK\n STRAIGHT_LINK = LinkRenderType.STRAIGHT_LINK\n LINEAR_LINK = LinkRenderType.LINEAR_LINK\n SPLINE_LINK = LinkRenderType.SPLINE_LINK\n\n NORMAL_TITLE = TitleMode.NORMAL_TITLE\n NO_TITLE = TitleMode.NO_TITLE\n TRANSPARENT_TITLE = TitleMode.TRANSPARENT_TITLE\n AUTOHIDE_TITLE = TitleMode.AUTOHIDE_TITLE\n\n /** arrange nodes vertically */\n VERTICAL_LAYOUT = 'vertical'\n\n /** used to redirect calls */\n proxy = null\n node_images_path = ''\n\n debug = false\n catch_exceptions = true\n throw_errors = true\n /** if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits */\n allow_scripts = false\n /** nodetypes by string */\n registered_node_types: Record<string, typeof LGraphNode> = {}\n /** @deprecated used for dropping files in the canvas. It appears the code that enables this was removed, but the object remains and is references by built-in drag drop. */\n node_types_by_file_extension: Record<string, { type: string }> = {}\n /** node types by classname */\n Nodes: Record<string, typeof LGraphNode> = {}\n /** used to store vars between graphs */\n Globals = {}\n\n /** @deprecated Unused and will be deleted. */\n searchbox_extras: Dictionary<unknown> = {}\n\n /** [true!] this make the nodes box (top left circle) coloured when triggered (execute/action), visual feedback */\n node_box_coloured_when_on = false\n /** [true!] nodebox based on node mode, visual feedback */\n node_box_coloured_by_mode = false\n\n /** [false on mobile] better true if not touch device, TODO add an helper/listener to close if false */\n dialog_close_on_mouse_leave = false\n dialog_close_on_mouse_leave_delay = 500\n\n /** [false!] prefer false if results too easy to break links - implement with ALT or TODO custom keys */\n shift_click_do_break_link_from = false\n /** [false!]prefer false, way too easy to break links */\n click_do_break_link_to = false\n /** [true!] who accidentally ctrl-alt-clicks on an in/output? nobody! that's who! */\n ctrl_alt_click_do_break_link = true\n /** [true!] snaps links when dragging connections over valid targets */\n snaps_for_comfy = true\n /** [true!] renders a partial border to highlight when a dragged link is snapped to a node */\n snap_highlights_node = true\n\n /**\n * If `true`, items always snap to the grid - modifier keys are ignored.\n * When {@link snapToGrid} is falsy, a value of `1` is used.\n * Default: `false`\n */\n alwaysSnapToGrid?: boolean\n\n /**\n * When set to a positive number, when nodes are moved their positions will\n * be rounded to the nearest multiple of this value. Half up.\n * Default: `undefined`\n * @todo Not implemented - see {@link LiteGraph.CANVAS_GRID_SIZE}\n */\n snapToGrid?: number\n\n /** [false on mobile] better true if not touch device, TODO add an helper/listener to close if false */\n search_hide_on_mouse_leave = true\n /**\n * [true!] enable filtering slots type in the search widget\n * !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out]\n */\n search_filter_enabled = false\n /** [true!] opens the results list when opening the search widget */\n search_show_all_on_open = true\n\n /**\n * [if want false, use true, run, get vars values to be statically set, than disable]\n * nodes types and nodeclass association with node types need to be calculated,\n * if dont want this, calculate once and set registered_slot_[in/out]_types and slot_types_[in/out]\n */\n auto_load_slot_types = false\n\n // set these values if not using auto_load_slot_types\n /** slot types for nodeclass */\n registered_slot_in_types: Record<string, { nodes: string[] }> = {}\n /** slot types for nodeclass */\n registered_slot_out_types: Record<string, { nodes: string[] }> = {}\n /** slot types IN */\n slot_types_in: string[] = []\n /** slot types OUT */\n slot_types_out: string[] = []\n /**\n * specify for each IN slot type a(/many) default node(s), use single string, array, or object\n * (with node, title, parameters, ..) like for search\n */\n slot_types_default_in: Record<string, string[]> = {}\n /**\n * specify for each OUT slot type a(/many) default node(s), use single string, array, or object\n * (with node, title, parameters, ..) like for search\n */\n slot_types_default_out: Record<string, string[]> = {}\n\n /** [true!] very handy, ALT click to clone and drag the new node */\n alt_drag_do_clone_nodes = false\n\n /**\n * [true!] will create and connect event slots when using action/events connections,\n * !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this\n */\n do_add_triggers_slots = false\n\n /** [false!] being events, it is strongly recommended to use them sequentially, one by one */\n allow_multi_output_for_events = true\n\n /** [true!] allows to create and connect a node clicking with the third button (wheel) */\n middle_click_slot_add_default_node = false\n\n /** [true!] dragging a link to empty space will open a menu, add from list, search or defaults */\n release_link_on_empty_shows_menu = false\n\n /** \"mouse\"|\"pointer\" use mouse for retrocompatibility issues? (none found @ now) */\n pointerevents_method = 'pointer'\n\n /**\n * [true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected\n * with the inputs of the newly pasted nodes\n */\n ctrl_shift_v_paste_connect_unselected_outputs = true\n\n // if true, all newly created nodes/links will use string UUIDs for their id fields instead of integers.\n // use this if you must have node IDs that are unique across all graphs and subgraphs.\n use_uuids = false\n\n // Whether to highlight the bounding box of selected groups\n highlight_selected_group = true\n\n /** Whether to scale context with the graph when zooming in. Zooming out never makes context menus smaller. */\n context_menu_scaling = false\n\n /**\n * Debugging flag. Repeats deprecation warnings every time they are reported.\n * May impact performance.\n */\n alwaysRepeatWarnings: boolean = false\n\n /**\n * Array of callbacks to execute when Litegraph first reports a deprecated API being used.\n * @see alwaysRepeatWarnings By default, will not repeat identical messages.\n */\n onDeprecationWarning: ((message: string, source?: object) => void)[] = [\n console.warn\n ]\n\n /**\n * @deprecated Removed; has no effect.\n * If `true`, mouse wheel events will be interpreted as trackpad gestures.\n * Tested on MacBook M4 Pro.\n * @default false\n * @see macGesturesRequireMac\n */\n macTrackpadGestures: boolean = false\n\n /**\n * @deprecated Removed; has no effect.\n * If both this setting and {@link macTrackpadGestures} are `true`, trackpad gestures will\n * only be enabled when the browser user agent includes \"Mac\".\n * @default true\n * @see macTrackpadGestures\n */\n macGesturesRequireMac: boolean = true\n\n /**\n * \"standard\": change the dragging on left mouse button click to select, enable middle-click or spacebar+left-click dragging\n * \"legacy\": Enable dragging on left-click (original behavior)\n * \"custom\": Use leftMouseClickBehavior and mouseWheelScroll settings\n * @default \"legacy\"\n */\n canvasNavigationMode: 'standard' | 'legacy' | 'custom' = 'legacy'\n\n leftMouseClickBehavior: 'panning' | 'select' = 'panning'\n\n mouseWheelScroll: 'panning' | 'zoom' = 'panning'\n\n /**\n * If `true`, widget labels and values will both be truncated (proportionally to size),\n * until they fit within the widget.\n *\n * Otherwise, the label will be truncated completely before the value is truncated.\n * @default false\n */\n truncateWidgetTextEvenly: boolean = false\n\n /**\n * If `true`, widget values will be completely truncated when shrinking a widget,\n * before truncating widget labels. {@link truncateWidgetTextEvenly} must be `false`.\n * @default false\n */\n truncateWidgetValuesFirst: boolean = false\n\n /**\n * If `true`, the current viewport scale & offset of the first attached canvas will be included with the graph when exporting.\n * @default true\n */\n saveViewportWithGraph: boolean = true\n\n /**\n * Enable Vue nodes mode for rendering and positioning.\n * When true:\n * - Nodes will calculate slot positions using Vue component dimensions\n * - LiteGraph will skip rendering node bodies entirely\n * - Vue components will handle all node rendering\n * - LiteGraph continues to render connections, links, and graph background\n * This should be set by the frontend when the Vue nodes feature is enabled.\n * @default false\n */\n vueNodesMode: boolean = false\n\n // Special Rendering Values pulled out of app.ts patches\n nodeOpacity = 1\n nodeLightness: number | undefined = undefined\n\n // TODO: Remove legacy accessors\n LGraph = LGraph\n LLink = LLink\n LGraphNode = LGraphNode\n LGraphGroup = LGraphGroup\n DragAndScale = DragAndScale\n LGraphCanvas = LGraphCanvas\n ContextMenu = ContextMenu\n CurveEditor = CurveEditor\n Reroute = Reroute\n\n constructor() {\n Object.defineProperty(this, 'Classes', { writable: false })\n }\n\n Classes = {\n get SubgraphSlot() {\n return SubgraphSlot\n },\n get SubgraphIONodeBase() {\n return SubgraphIONodeBase\n },\n\n // Rich drawing\n get Rectangle() {\n return Rectangle\n },\n\n // Debug / helpers\n get InputIndicators() {\n return InputIndicators\n }\n }\n\n onNodeTypeRegistered?(type: string, base_class: typeof LGraphNode): void\n onNodeTypeReplaced?(\n type: string,\n base_class: typeof LGraphNode,\n prev: unknown\n ): void\n\n /**\n * Register a node class so it can be listed when the user wants to create a new one\n * @param type name of the node and path\n * @param base_class class containing the structure of a node\n */\n registerNodeType(type: string, base_class: typeof LGraphNode): void {\n if (!base_class.prototype)\n throw 'Cannot register a simple object, it must be a class with a prototype'\n base_class.type = type\n\n const classname = base_class.name\n\n const pos = type.lastIndexOf('/')\n base_class.category = type.substring(0, pos)\n\n base_class.title ||= classname\n\n // extend class\n for (const i in LGraphNode.prototype) {\n // @ts-expect-error #576 This functionality is deprecated and should be removed.\n base_class.prototype[i] ||= LGraphNode.prototype[i]\n }\n\n const prev = this.registered_node_types[type]\n if (prev && this.debug) {\n console.warn('replacing node type:', type)\n }\n\n this.registered_node_types[type] = base_class\n if (base_class.constructor.name) this.Nodes[classname] = base_class\n\n this.onNodeTypeRegistered?.(type, base_class)\n if (prev) this.onNodeTypeReplaced?.(type, base_class, prev)\n\n // warnings\n if (base_class.prototype.onPropertyChange)\n console.warn(\n `LiteGraph node class ${type} has onPropertyChange method, it must be called onPropertyChanged with d at the end`\n )\n\n // TODO one would want to know input and output :: this would allow through registerNodeAndSlotType to get all the slots types\n if (this.auto_load_slot_types) new base_class(base_class.title || 'tmpnode')\n }\n\n /**\n * removes a node type from the system\n * @param type name of the node or the node constructor itself\n */\n unregisterNodeType(type: string | typeof LGraphNode): void {\n const base_class =\n typeof type === 'string' ? this.registered_node_types[type] : type\n if (!base_class) throw `node type not found: ${String(type)}`\n\n delete this.registered_node_types[String(base_class.type)]\n\n const name = base_class.constructor.name\n if (name) delete this.Nodes[name]\n }\n\n /**\n * Save a slot type and his node\n * @param type name of the node or the node constructor itself\n * @param slot_type name of the slot type (variable type), eg. string, number, array, boolean, ..\n */\n registerNodeAndSlotType(\n type: LGraphNode,\n slot_type: ISlotType,\n out?: boolean\n ): void {\n out ||= false\n const base_class =\n typeof type === 'string' &&\n // @ts-expect-error Confirm this function no longer supports string types - base_class should always be an instance not a constructor.\n this.registered_node_types[type] !== 'anonymous'\n ? this.registered_node_types[type]\n : type\n\n // @ts-expect-error Confirm this function no longer supports string types - base_class should always be an instance not a constructor.\n const class_type = base_class.constructor.type\n\n let allTypes = []\n if (typeof slot_type === 'string') {\n allTypes = slot_type.split(',')\n } else if (slot_type == this.EVENT || slot_type == this.ACTION) {\n allTypes = ['_event_']\n } else {\n allTypes = ['*']\n }\n\n for (let slotType of allTypes) {\n if (slotType === '') slotType = '*'\n\n const register = out\n ? this.registered_slot_out_types\n : this.registered_slot_in_types\n register[slotType] ??= { nodes: [] }\n\n const { nodes } = register[slotType]\n if (!nodes.includes(class_type)) nodes.push(class_type)\n\n // check if is a new type\n const types = out ? this.slot_types_out : this.slot_types_in\n const type = slotType.toLowerCase()\n\n if (!types.includes(type)) {\n types.push(type)\n types.sort()\n }\n }\n }\n\n /**\n * Removes all previously registered node's types\n */\n clearRegisteredTypes(): void {\n this.registered_node_types = {}\n this.node_types_by_file_extension = {}\n this.Nodes = {}\n this.searchbox_extras = {}\n }\n\n /**\n * Create a node of a given type with a name. The node is not attached to any graph yet.\n * @param type full name of the node class. p.e. \"math/sin\"\n * @param title a name to distinguish from other nodes\n * @param options to set options\n */\n createNode(\n type: string,\n title?: string,\n options?: Dictionary<unknown>\n ): LGraphNode | null {\n const base_class = this.registered_node_types[type]\n if (!base_class) {\n if (this.debug) console.warn(`GraphNode type \"${type}\" not registered.`)\n return null\n }\n\n title = title || base_class.title || type\n\n let node = null\n\n if (this.catch_exceptions) {\n try {\n node = new base_class(title)\n } catch (error) {\n console.error(error)\n return null\n }\n } else {\n node = new base_class(title)\n }\n\n node.type = type\n\n if (!node.title && title) node.title = title\n node.properties ||= {}\n node.properties_info ||= []\n node.flags ||= {}\n // call onresize?\n node.size ||= node.computeSize()\n node.pos ||= [this.DEFAULT_POSITION[0], this.DEFAULT_POSITION[1]]\n node.mode ||= LGraphEventMode.ALWAYS\n\n // extra options\n if (options) {\n for (const i in options) {\n // @ts-expect-error #577 Requires interface\n node[i] = options[i]\n }\n }\n\n // callback\n node.onNodeCreated?.()\n return node\n }\n\n /**\n * Returns a registered node type with a given name\n * @param type full name of the node class. p.e. \"math/sin\"\n * @returns the node class\n */\n getNodeType(type: string): typeof LGraphNode {\n return this.registered_node_types[type]\n }\n\n /**\n * Returns a list of node types matching one category\n * @param category category name\n * @returns array with all the node classes\n */\n getNodeTypesInCategory(category: string, filter?: string) {\n const r = []\n for (const i in this.registered_node_types) {\n const type = this.registered_node_types[i]\n if (type.filter != filter) continue\n\n if (category == '') {\n if (type.category == null) r.push(type)\n } else if (type.category == category) {\n r.push(type)\n }\n }\n\n return r\n }\n\n /**\n * Returns a list with all the node type categories\n * @param filter only nodes with ctor.filter equal can be shown\n * @returns array with all the names of the categories\n */\n getNodeTypesCategories(filter?: string): string[] {\n const categories: Dictionary<number> = { '': 1 }\n for (const i in this.registered_node_types) {\n const type = this.registered_node_types[i]\n if (type.category && !type.skip_list) {\n if (type.filter != filter) continue\n\n categories[type.category] = 1\n }\n }\n const result = []\n for (const i in categories) {\n result.push(i)\n }\n return result\n }\n\n // debug purposes: reloads all the js scripts that matches a wildcard\n reloadNodes(folder_wildcard: string): void {\n const tmp = document.getElementsByTagName('script')\n // weird, this array changes by its own, so we use a copy\n const script_files = []\n for (const element of tmp) {\n script_files.push(element)\n }\n\n const docHeadObj = document.getElementsByTagName('head')[0]\n folder_wildcard = document.location.href + folder_wildcard\n\n for (const script_file of script_files) {\n const src = script_file.src\n if (!src || src.substr(0, folder_wildcard.length) != folder_wildcard)\n continue\n\n try {\n const dynamicScript = document.createElement('script')\n dynamicScript.type = 'text/javascript'\n dynamicScript.src = src\n docHeadObj.append(dynamicScript)\n script_file.remove()\n } catch (error) {\n if (this.throw_errors) throw error\n if (this.debug) console.error('Error while reloading', src)\n }\n }\n }\n\n // separated just to improve if it doesn't work\n /** @deprecated Prefer {@link structuredClone} */\n cloneObject<T extends object | undefined | null>(\n obj: T,\n target?: T\n ): WhenNullish<T, null> {\n if (obj == null) return null as WhenNullish<T, null>\n\n const r = JSON.parse(JSON.stringify(obj))\n if (!target) return r\n\n for (const i in r) {\n // @ts-expect-error deprecated\n target[i] = r[i]\n }\n return target\n }\n\n /** @see {@link createUuidv4} @inheritdoc */\n uuidv4 = createUuidv4\n\n /**\n * Returns if the types of two slots are compatible (taking into account wildcards, etc)\n * @param type_a output\n * @param type_b input\n * @returns true if they can be connected\n */\n isValidConnection(type_a: ISlotType, type_b: ISlotType): boolean {\n if (type_a == '' || type_a === '*') type_a = 0\n if (type_b == '' || type_b === '*') type_b = 0\n // If generic in/output, matching types (valid for triggers), or event/action types\n if (\n !type_a ||\n !type_b ||\n type_a == type_b ||\n (type_a == this.EVENT && type_b == this.ACTION)\n ) {\n return true\n }\n\n // Enforce string type to handle toLowerCase call (-1 number not ok)\n type_a = String(type_a)\n type_b = String(type_b)\n type_a = type_a.toLowerCase()\n type_b = type_b.toLowerCase()\n\n // For nodes supporting multiple connection types\n if (!type_a.includes(',') && !type_b.includes(',')) return type_a == type_b\n\n // Check all permutations to see if one is valid\n const supported_types_a = type_a.split(',')\n const supported_types_b = type_b.split(',')\n for (const a of supported_types_a) {\n for (const b of supported_types_b) {\n if (this.isValidConnection(a, b)) return true\n }\n }\n\n return false\n }\n\n // used to create nodes from wrapping functions\n getParameterNames(func: (...args: unknown[]) => unknown): string[] {\n return String(func)\n .replaceAll(/\\/\\/.*$/gm, '') // strip single-line comments\n .replaceAll(/\\s+/g, '') // strip white space\n .replaceAll(/\\/\\*[^*/]*\\*\\//g, '') // strip multi-line comments /**/\n .split('){', 1)[0]\n .replace(/^[^(]*\\(/, '') // extract the parameters\n .replaceAll(/=[^,]+/g, '') // strip any ES6 defaults\n .split(',')\n .filter(Boolean) // split & filter [\"\"]\n }\n\n /* helper for interaction: pointer, touch, mouse Listeners\n used by LGraphCanvas DragAndScale ContextMenu */\n pointerListenerAdd(\n oDOM: Node,\n sEvIn: string,\n fCall: (e: Event) => boolean | void,\n capture = false\n ): void {\n if (\n !oDOM ||\n !oDOM.addEventListener ||\n !sEvIn ||\n typeof fCall !== 'function'\n )\n return\n\n let sMethod = this.pointerevents_method\n let sEvent = sEvIn\n\n // UNDER CONSTRUCTION\n // convert pointerevents to touch event when not available\n if (sMethod == 'pointer' && !window.PointerEvent) {\n console.warn(\"sMethod=='pointer' && !window.PointerEvent\")\n console.warn(\n `Converting pointer[${sEvent}] : down move up cancel enter TO touchstart touchmove touchend, etc ..`\n )\n switch (sEvent) {\n case 'down': {\n sMethod = 'touch'\n sEvent = 'start'\n break\n }\n case 'move': {\n sMethod = 'touch'\n // sEvent = \"move\";\n break\n }\n case 'up': {\n sMethod = 'touch'\n sEvent = 'end'\n break\n }\n case 'cancel': {\n sMethod = 'touch'\n // sEvent = \"cancel\";\n break\n }\n case 'enter': {\n // TODO: Determine if a move event should be sent\n break\n }\n // case \"over\": case \"out\": not used at now\n default: {\n console.warn(\n `PointerEvent not available in this browser ? The event ${sEvent} would not be called`\n )\n }\n }\n }\n\n switch (sEvent) {\n // both pointer and move events\n case 'down':\n case 'up':\n case 'move':\n case 'over':\n case 'out':\n // @ts-expect-error - intentional fallthrough\n case 'enter': {\n oDOM.addEventListener(sMethod + sEvent, fCall, capture)\n }\n // only pointerevents\n // falls through\n case 'leave':\n case 'cancel':\n case 'gotpointercapture':\n // @ts-expect-error - intentional fallthrough\n case 'lostpointercapture': {\n if (sMethod != 'mouse') {\n return oDOM.addEventListener(sMethod + sEvent, fCall, capture)\n }\n }\n // not \"pointer\" || \"mouse\"\n // falls through\n default:\n return oDOM.addEventListener(sEvent, fCall, capture)\n }\n }\n\n pointerListenerRemove(\n oDOM: Node,\n sEvent: string,\n fCall: (e: Event) => boolean | void,\n capture = false\n ): void {\n if (\n !oDOM ||\n !oDOM.removeEventListener ||\n !sEvent ||\n typeof fCall !== 'function'\n )\n return\n\n switch (sEvent) {\n // both pointer and move events\n case 'down':\n case 'up':\n case 'move':\n case 'over':\n case 'out':\n // @ts-expect-error - intentional fallthrough\n case 'enter': {\n if (\n this.pointerevents_method == 'pointer' ||\n this.pointerevents_method == 'mouse'\n ) {\n oDOM.removeEventListener(\n this.pointerevents_method + sEvent,\n fCall,\n capture\n )\n }\n }\n // only pointerevents\n // falls through\n case 'leave':\n case 'cancel':\n case 'gotpointercapture':\n // @ts-expect-error - intentional fallthrough\n case 'lostpointercapture': {\n if (this.pointerevents_method == 'pointer') {\n return oDOM.removeEventListener(\n this.pointerevents_method + sEvent,\n fCall,\n capture\n )\n }\n }\n // not \"pointer\" || \"mouse\"\n // falls through\n default:\n return oDOM.removeEventListener(sEvent, fCall, capture)\n }\n }\n\n getTime(): number {\n return performance.now()\n }\n\n distance = distance\n\n colorToString(c: [number, number, number, number]): string {\n return `rgba(${Math.round(c[0] * 255).toFixed()},${Math.round(\n c[1] * 255\n ).toFixed()},${Math.round(c[2] * 255).toFixed()},${\n c.length == 4 ? c[3].toFixed(2) : '1.0'\n })`\n }\n\n isInsideRectangle = isInsideRectangle\n\n // [minx,miny,maxx,maxy]\n growBounding(bounding: Rect, x: number, y: number): void {\n if (x < bounding[0]) {\n bounding[0] = x\n } else if (x > bounding[2]) {\n bounding[2] = x\n }\n\n if (y < bounding[1]) {\n bounding[1] = y\n } else if (y > bounding[3]) {\n bounding[3] = y\n }\n }\n\n overlapBounding = overlapBounding\n\n // point inside bounding box\n isInsideBounding(p: number[], bb: number[][]): boolean {\n if (\n p[0] < bb[0][0] ||\n p[1] < bb[0][1] ||\n p[0] > bb[1][0] ||\n p[1] > bb[1][1]\n ) {\n return false\n }\n return true\n }\n\n // Convert a hex value to its decimal value - the inputted hex must be in the\n // format of a hex triplet - the kind we use for HTML colours. The function\n // will return an array with three values.\n hex2num(hex: string): number[] {\n if (hex.charAt(0) == '#') {\n hex = hex.slice(1)\n // Remove the '#' char - if there is one.\n }\n hex = hex.toUpperCase()\n const hex_alphabets = '0123456789ABCDEF'\n const value = new Array(3)\n let k = 0\n let int1, int2\n for (let i = 0; i < 6; i += 2) {\n int1 = hex_alphabets.indexOf(hex.charAt(i))\n int2 = hex_alphabets.indexOf(hex.charAt(i + 1))\n value[k] = int1 * 16 + int2\n k++\n }\n return value\n }\n\n // Give a array with three values as the argument and the function will return\n // the corresponding hex triplet.\n num2hex(triplet: number[]): string {\n const hex_alphabets = '0123456789ABCDEF'\n let hex = '#'\n let int1, int2\n for (let i = 0; i < 3; i++) {\n int1 = triplet[i] / 16\n int2 = triplet[i] % 16\n\n hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2)\n }\n return hex\n }\n\n closeAllContextMenus(ref_window: Window = window): void {\n const elements = [\n ...ref_window.document.querySelectorAll('.litecontextmenu')\n ]\n if (!elements.length) return\n\n for (const element of elements) {\n if ('close' in element && typeof element.close === 'function') {\n element.close()\n } else {\n element.remove()\n }\n }\n }\n\n extendClass(\n target: Record<string, unknown> & { prototype?: object },\n origin: Record<string, unknown> & { prototype?: object }\n ): void {\n for (const i in origin) {\n // copy class properties\n // eslint-disable-next-line no-prototype-builtins\n if (target.hasOwnProperty(i)) continue\n target[i] = origin[i]\n }\n\n if (origin.prototype && target.prototype) {\n const originProto = origin.prototype as Record<string, unknown>\n const targetProto = target.prototype as Record<string, unknown>\n\n // copy prototype properties\n for (const i in originProto) {\n // only enumerable\n // eslint-disable-next-line no-prototype-builtins\n if (!originProto.hasOwnProperty(i)) continue\n\n // avoid overwriting existing ones\n // eslint-disable-next-line no-prototype-builtins\n if (targetProto.hasOwnProperty(i)) continue\n\n // Use Object.getOwnPropertyDescriptor to copy getters/setters properly\n const descriptor = Object.getOwnPropertyDescriptor(originProto, i)\n if (descriptor) {\n Object.defineProperty(targetProto, i, descriptor)\n }\n }\n }\n }\n}\n","// @ts-expect-error Polyfill\nSymbol.dispose ??= Symbol('Symbol.dispose')\n// @ts-expect-error Polyfill\nSymbol.asyncDispose ??= Symbol('Symbol.asyncDispose')\n\n// API *************************************************\n// like rect but rounded corners\nexport function loadPolyfills() {\n if (\n typeof window != 'undefined' &&\n window.CanvasRenderingContext2D &&\n !window.CanvasRenderingContext2D.prototype.roundRect\n ) {\n // @ts-expect-error Slightly broken polyfill - radius_low not impl. anywhere\n window.CanvasRenderingContext2D.prototype.roundRect = function (\n x: number,\n y: number,\n w: number,\n h: number,\n radius: number | number[],\n radius_low: number | number[]\n ) {\n let top_left_radius = 0\n let top_right_radius = 0\n let bottom_left_radius = 0\n let bottom_right_radius = 0\n\n if (radius === 0) {\n this.rect(x, y, w, h)\n return\n }\n\n if (radius_low === undefined) radius_low = radius\n\n // make it compatible with official one\n if (Array.isArray(radius)) {\n if (radius.length == 1) {\n top_left_radius =\n top_right_radius =\n bottom_left_radius =\n bottom_right_radius =\n radius[0]\n } else if (radius.length == 2) {\n top_left_radius = bottom_right_radius = radius[0]\n top_right_radius = bottom_left_radius = radius[1]\n } else if (radius.length == 4) {\n top_left_radius = radius[0]\n top_right_radius = radius[1]\n bottom_left_radius = radius[2]\n bottom_right_radius = radius[3]\n } else {\n return\n }\n } else {\n // old using numbers\n top_left_radius = radius || 0\n top_right_radius = radius || 0\n\n const low = !Array.isArray(radius_low) && radius_low ? radius_low : 0\n bottom_left_radius = low\n bottom_right_radius = low\n }\n\n // top right\n this.moveTo(x + top_left_radius, y)\n this.lineTo(x + w - top_right_radius, y)\n this.quadraticCurveTo(x + w, y, x + w, y + top_right_radius)\n\n // bottom right\n this.lineTo(x + w, y + h - bottom_right_radius)\n this.quadraticCurveTo(x + w, y + h, x + w - bottom_right_radius, y + h)\n\n // bottom left\n this.lineTo(x + bottom_right_radius, y + h)\n this.quadraticCurveTo(x, y + h, x, y + h - bottom_left_radius)\n\n // top left\n this.lineTo(x, y + bottom_left_radius)\n this.quadraticCurveTo(x, y, x + top_left_radius, y)\n }\n }\n\n if (typeof window != 'undefined' && !window['requestAnimationFrame']) {\n window.requestAnimationFrame =\n // @ts-expect-error Legacy code\n window.webkitRequestAnimationFrame ||\n // @ts-expect-error Legacy code\n window.mozRequestAnimationFrame ||\n function (callback) {\n window.setTimeout(callback, 1000 / 60)\n }\n }\n}\n","import type { ContextMenu } from './ContextMenu'\nimport type { LGraphNode } from './LGraphNode'\nimport { LiteGraphGlobal } from './LiteGraphGlobal'\nimport type {\n ConnectingLink,\n IContextMenuOptions,\n Point,\n Size\n} from './interfaces'\nimport { loadPolyfills } from './polyfills'\nimport type { CanvasEventDetail } from './types/events'\nimport type { RenderShape, TitleMode } from './types/globalEnums'\n\n// Must remain above LiteGraphGlobal (circular dependency due to abstract factory behaviour in `configure`)\nexport { Subgraph } from './subgraph/Subgraph'\n\nexport const LiteGraph = new LiteGraphGlobal()\n\n// Load legacy polyfills\nloadPolyfills()\n\n// Backwards compat\n\n// Type definitions for litegraph.js 0.7.0\n// Project: litegraph.js\n// Definitions by: NateScarlet <https://github.com/NateScarlet>\n/** @deprecated Use {@link Point} instead. */\nexport type Vector2 = Point\n\ninterface IContextMenuItem {\n content: string\n callback?: ContextMenuEventListener\n /** Used as innerHTML for extra child element */\n title?: string\n disabled?: boolean\n has_submenu?: boolean\n submenu?: {\n options: IContextMenuItem[]\n } & IContextMenuOptions\n className?: string\n}\n\ntype ContextMenuEventListener = (\n value: IContextMenuItem,\n options: IContextMenuOptions,\n event: MouseEvent,\n parentMenu: ContextMenu<unknown> | undefined,\n node: LGraphNode\n) => boolean | void\n\nexport interface LinkReleaseContextExtended {\n links: ConnectingLink[]\n}\n\nexport interface LiteGraphCanvasEvent extends CustomEvent<CanvasEventDetail> {}\n\nexport interface LGraphNodeConstructor<T extends LGraphNode = LGraphNode> {\n new (title: string, type?: string): T\n\n title: string\n type?: string // TODO: to be, or not to be--that is the question\n size?: Size\n min_height?: number\n slot_start_y?: number\n widgets_info?: Record<string, unknown>\n collapsable?: boolean\n color?: string\n bgcolor?: string\n shape?: RenderShape\n title_mode?: TitleMode\n title_color?: string\n title_text_color?: string\n keepAllLinksOnBypass: boolean\n resizeHandleSize?: number\n resizeEdgeSize?: number\n}\n\n// End backwards compat\n\nexport { LinkConnector } from './canvas/LinkConnector'\nexport { isOverNodeInput, isOverNodeOutput } from './canvas/measureSlots'\nexport { CanvasPointer } from './CanvasPointer'\nexport * as Constants from './constants'\nexport { SUBGRAPH_INPUT_ID } from './constants'\nexport { ContextMenu } from './ContextMenu'\n\nexport { DragAndScale } from './DragAndScale'\n\nexport { Rectangle } from './infrastructure/Rectangle'\nexport { RecursionError } from './infrastructure/RecursionError'\nexport type {\n CanvasColour,\n ColorOption,\n IContextMenuOptions,\n IContextMenuValue,\n INodeInputSlot,\n INodeOutputSlot,\n INodeSlot,\n ISlotType,\n LinkNetwork,\n Point,\n Positionable,\n Size\n} from './interfaces'\nexport {\n LGraph,\n type LGraphTriggerAction,\n type LGraphTriggerParam\n} from './LGraph'\nexport type { LGraphTriggerEvent } from './types/graphTriggers'\nexport { BadgePosition, LGraphBadge } from './LGraphBadge'\nexport { LGraphCanvas } from './LGraphCanvas'\nexport { LGraphGroup } from './LGraphGroup'\nexport { LGraphNode, type NodeId } from './LGraphNode'\nexport { LLink } from './LLink'\nexport { createBounds } from './measure'\nexport { Reroute, type RerouteId } from './Reroute'\nexport {\n type ExecutableLGraphNode,\n ExecutableNodeDTO,\n type ExecutionId\n} from './subgraph/ExecutableNodeDTO'\nexport { SubgraphNode } from './subgraph/SubgraphNode'\nexport type { CanvasPointerEvent } from './types/events'\nexport {\n EaseFunction,\n LGraphEventMode,\n LinkDirection,\n LinkMarkerShape,\n RenderShape\n} from './types/globalEnums'\nexport type {\n ExportedSubgraph,\n ExportedSubgraphInstance,\n ISerialisedGraph,\n ISerialisedNode,\n SerialisableGraph\n} from './types/serialisation'\nexport type { IWidget } from './types/widgets'\nexport { isColorable } from './utils/type'\nexport { createUuidv4 } from './utils/uuid'\nexport type { UUID } from './utils/uuid'\nexport { truncateText } from './utils/textUtils'\nexport { getWidgetStep } from './utils/widget'\nexport { distributeSpace, type SpaceRequest } from './utils/spaceDistribution'\n\nexport { BaseWidget } from './widgets/BaseWidget'\n\nexport { LegacyWidget } from './widgets/LegacyWidget'\n\nexport { isComboWidget, isAssetWidget } from './widgets/widgetMap'\n// Additional test-specific exports\nexport { LGraphButton } from './LGraphButton'\nexport { MovingOutputLink } from './canvas/MovingOutputLink'\nexport { ToOutputRenderLink } from './canvas/ToOutputRenderLink'\nexport { ToInputFromIoNodeLink } from './canvas/ToInputFromIoNodeLink'\nexport type { TWidgetType, TWidgetValue, IWidgetOptions } from './types/widgets'\nexport {\n findUsedSubgraphIds,\n getDirectSubgraphIds,\n isSubgraphInput,\n isSubgraphOutput\n} from './subgraph/subgraphUtils'\nexport { NodeInputSlot } from './node/NodeInputSlot'\nexport { NodeOutputSlot } from './node/NodeOutputSlot'\nexport { inputAsSerialisable, outputAsSerialisable } from './node/slotUtils'\nexport { MovingInputLink } from './canvas/MovingInputLink'\nexport { ToInputRenderLink } from './canvas/ToInputRenderLink'\nexport { LiteGraphGlobal } from './LiteGraphGlobal'\n","import { z } from 'zod'\n\nimport { LiteGraph } from '@/lib/litegraph/src/litegraph'\n\nconst nodeSlotSchema = z.object({\n CLIP: z.string(),\n CLIP_VISION: z.string(),\n CLIP_VISION_OUTPUT: z.string(),\n CONDITIONING: z.string(),\n CONTROL_NET: z.string(),\n IMAGE: z.string(),\n LATENT: z.string(),\n MASK: z.string(),\n MODEL: z.string(),\n STYLE_MODEL: z.string(),\n VAE: z.string(),\n NOISE: z.string(),\n GUIDER: z.string(),\n SAMPLER: z.string(),\n SIGMAS: z.string(),\n TAESD: z.string()\n})\n\nconst litegraphBaseSchema = z.object({\n BACKGROUND_IMAGE: z.string(),\n CLEAR_BACKGROUND_COLOR: z.string(),\n NODE_TITLE_COLOR: z.string(),\n NODE_SELECTED_TITLE_COLOR: z.string(),\n NODE_TEXT_SIZE: z.number(),\n NODE_TEXT_COLOR: z.string(),\n NODE_TEXT_HIGHLIGHT_COLOR: z.string(),\n NODE_SUBTEXT_SIZE: z.number(),\n NODE_DEFAULT_COLOR: z.string(),\n NODE_DEFAULT_BGCOLOR: z.string(),\n NODE_DEFAULT_BOXCOLOR: z.string(),\n NODE_DEFAULT_SHAPE: z.union([\n z.literal(LiteGraph.BOX_SHAPE),\n z.literal(LiteGraph.ROUND_SHAPE),\n z.literal(LiteGraph.CARD_SHAPE),\n // Legacy palettes have string field for NODE_DEFAULT_SHAPE.\n z.string()\n ]),\n NODE_BOX_OUTLINE_COLOR: z.string(),\n NODE_BYPASS_BGCOLOR: z.string(),\n NODE_ERROR_COLOUR: z.string(),\n DEFAULT_SHADOW_COLOR: z.string(),\n DEFAULT_GROUP_FONT: z.number(),\n WIDGET_BGCOLOR: z.string(),\n WIDGET_OUTLINE_COLOR: z.string(),\n WIDGET_TEXT_COLOR: z.string(),\n WIDGET_SECONDARY_TEXT_COLOR: z.string(),\n WIDGET_DISABLED_TEXT_COLOR: z.string(),\n LINK_COLOR: z.string(),\n EVENT_LINK_COLOR: z.string(),\n CONNECTING_LINK_COLOR: z.string(),\n BADGE_FG_COLOR: z.string(),\n BADGE_BG_COLOR: z.string()\n})\n\nexport const comfyBaseSchema = z.object({\n ['fg-color']: z.string(),\n ['bg-color']: z.string(),\n ['bg-img']: z.string().optional(),\n ['comfy-menu-bg']: z.string(),\n ['comfy-menu-secondary-bg']: z.string(),\n ['comfy-input-bg']: z.string(),\n ['input-text']: z.string(),\n ['descrip-text']: z.string(),\n ['drag-text']: z.string(),\n ['error-text']: z.string(),\n ['border-color']: z.string(),\n ['tr-even-bg-color']: z.string(),\n ['tr-odd-bg-color']: z.string(),\n ['content-bg']: z.string(),\n ['content-fg']: z.string(),\n ['content-hover-bg']: z.string(),\n ['content-hover-fg']: z.string(),\n ['bar-shadow']: z.string(),\n ['contrast-mix-color']: z.string().optional(),\n ['interface-stroke']: z.string().optional(),\n ['interface-panel-surface']: z.string().optional(),\n ['interface-panel-box-shadow']: z.string().optional(),\n ['interface-panel-drop-shadow']: z.string().optional(),\n ['interface-panel-hover-surface']: z.string().optional(),\n ['interface-panel-selected-surface']: z.string().optional(),\n ['interface-button-hover-surface']: z.string().optional()\n})\n\nconst colorsSchema = z.object({\n node_slot: nodeSlotSchema,\n litegraph_base: litegraphBaseSchema,\n comfy_base: comfyBaseSchema\n})\n\nconst partialColorsSchema = z.object({\n node_slot: nodeSlotSchema.partial(),\n litegraph_base: litegraphBaseSchema.partial(),\n comfy_base: comfyBaseSchema.partial()\n})\n\n// Palette in the wild can have custom metadata fields such as 'version'.\nexport const paletteSchema = z\n .object({\n id: z.string(),\n name: z.string(),\n colors: partialColorsSchema,\n light_theme: z.boolean().optional()\n })\n .passthrough()\n\nconst completedPaletteSchema = z\n .object({\n id: z.string(),\n name: z.string(),\n colors: colorsSchema\n })\n .passthrough()\n\nexport const colorPalettesSchema = z.record(paletteSchema)\n\nexport type Colors = z.infer<typeof colorsSchema>\nexport type Palette = z.infer<typeof paletteSchema>\nexport type CompletedPalette = z.infer<typeof completedPaletteSchema>\nexport type ColorPalettes = z.infer<typeof colorPalettesSchema>\n","import { z } from 'zod'\n\n// KeyCombo schema\nconst zKeyCombo = z.object({\n key: z.string(),\n ctrl: z.boolean().optional(),\n alt: z.boolean().optional(),\n shift: z.boolean().optional(),\n meta: z.boolean().optional()\n})\n\n// Keybinding schema\nexport const zKeybinding = z.object({\n commandId: z.string(),\n combo: zKeyCombo,\n // Optional target element ID to limit keybinding to.\n // Note: Currently only used to distinguish between global keybindings\n // and litegraph canvas keybindings.\n // Do NOT use this field in extensions as it has no effect.\n targetElementId: z.string().optional()\n})\n\n// Infer types from schemas\nexport type KeyCombo = z.infer<typeof zKeyCombo>\nexport type Keybinding = z.infer<typeof zKeybinding>\n","export enum NodeSourceType {\n Core = 'core',\n CustomNodes = 'custom_nodes',\n Blueprint = 'blueprint',\n Unknown = 'unknown'\n}\n\nexport type NodeSource = {\n type: NodeSourceType\n className: string\n displayText: string\n badgeText: string\n}\n\nconst UNKNOWN_NODE_SOURCE: NodeSource = {\n type: NodeSourceType.Unknown,\n className: 'comfy-unknown',\n displayText: 'Unknown',\n badgeText: '?'\n}\n\nconst shortenNodeName = (name: string) => {\n return name\n .replace(/^(ComfyUI-|ComfyUI_|Comfy-|Comfy_)/, '')\n .replace(/(-ComfyUI|_ComfyUI|-Comfy|_Comfy)$/, '')\n}\n\nexport const getNodeSource = (python_module?: string): NodeSource => {\n if (!python_module) {\n return UNKNOWN_NODE_SOURCE\n }\n const modules = python_module.split('.')\n if (['nodes', 'comfy_extras', 'comfy_api_nodes'].includes(modules[0])) {\n return {\n type: NodeSourceType.Core,\n className: 'comfy-core',\n displayText: 'Comfy Core',\n badgeText: '🦊'\n }\n } else if (modules[0] === 'blueprint') {\n return {\n type: NodeSourceType.Blueprint,\n className: 'blueprint',\n displayText: 'Blueprint',\n badgeText: 'bp'\n }\n } else if (modules[0] === 'custom_nodes') {\n const moduleName = modules[1]\n // Custom nodes installed via ComfyNodeRegistry will be in the format of\n // custom_nodes.<custom node name>@<version>\n const customNodeName = moduleName.split('@')[0]\n const displayName = shortenNodeName(customNodeName)\n return {\n type: NodeSourceType.CustomNodes,\n className: 'comfy-custom-nodes',\n displayText: displayName,\n badgeText: displayName\n }\n } else {\n return UNKNOWN_NODE_SOURCE\n }\n}\n\nexport enum NodeBadgeMode {\n None = 'None',\n ShowAll = 'Show all',\n HideBuiltIn = 'Hide built-in'\n}\n","export enum LinkReleaseTriggerAction {\n CONTEXT_MENU = 'context menu',\n SEARCH_BOX = 'search box',\n NO_ACTION = 'no action'\n}\n","import { z } from 'zod'\n\nimport { LinkMarkerShape } from '@/lib/litegraph/src/litegraph'\nimport { zNodeId } from '@/platform/workflow/validation/schemas/workflowSchema'\nimport { colorPalettesSchema } from '@/schemas/colorPaletteSchema'\nimport { zKeybinding } from '@/schemas/keyBindingSchema'\nimport { NodeBadgeMode } from '@/types/nodeSource'\nimport { LinkReleaseTriggerAction } from '@/types/searchBoxTypes'\n\nconst zNodeType = z.string()\nconst zPromptId = z.string()\nexport type PromptId = z.infer<typeof zPromptId>\nexport const resultItemType = z.enum(['input', 'output', 'temp'])\nexport type ResultItemType = z.infer<typeof resultItemType>\n\nconst zResultItem = z.object({\n filename: z.string().optional(),\n subfolder: z.string().optional(),\n type: resultItemType.optional()\n})\nexport type ResultItem = z.infer<typeof zResultItem>\nconst zOutputs = z\n .object({\n audio: z.array(zResultItem).optional(),\n images: z.array(zResultItem).optional(),\n video: z.array(zResultItem).optional(),\n animated: z.array(z.boolean()).optional(),\n text: z.union([z.string(), z.array(z.string())]).optional()\n })\n .passthrough()\n\nexport type NodeExecutionOutput = z.infer<typeof zOutputs>\n\nexport type NodeOutputWith<T extends Record<string, unknown>> =\n NodeExecutionOutput & T\n\n// WS messages\nconst zStatusWsMessageStatus = z.object({\n exec_info: z.object({\n queue_remaining: z.number().int()\n })\n})\n\nconst zStatusWsMessage = z.object({\n status: zStatusWsMessageStatus.nullish(),\n sid: z.string().nullish()\n})\n\nconst zProgressWsMessage = z.object({\n value: z.number().int(),\n max: z.number().int(),\n prompt_id: zPromptId,\n node: zNodeId\n})\n\nconst zNodeProgressState = z.object({\n value: z.number(),\n max: z.number(),\n state: z.enum(['pending', 'running', 'finished', 'error']),\n node_id: zNodeId,\n prompt_id: zPromptId,\n display_node_id: zNodeId.optional(),\n parent_node_id: zNodeId.optional(),\n real_node_id: zNodeId.optional()\n})\n\nconst zProgressStateWsMessage = z.object({\n prompt_id: zPromptId,\n nodes: z.record(zNodeId, zNodeProgressState)\n})\n\nconst zExecutingWsMessage = z.object({\n node: zNodeId,\n display_node: zNodeId,\n prompt_id: zPromptId\n})\n\nconst zExecutedWsMessage = zExecutingWsMessage.extend({\n output: zOutputs,\n merge: z.boolean().optional()\n})\n\nconst zExecutionWsMessageBase = z.object({\n prompt_id: zPromptId,\n timestamp: z.number().int()\n})\n\nconst zExecutionStartWsMessage = zExecutionWsMessageBase\nconst zExecutionSuccessWsMessage = zExecutionWsMessageBase\nconst zExecutionCachedWsMessage = zExecutionWsMessageBase.extend({\n nodes: z.array(zNodeId)\n})\nconst zExecutionInterruptedWsMessage = zExecutionWsMessageBase.extend({\n node_id: zNodeId,\n node_type: zNodeType,\n executed: z.array(zNodeId)\n})\nconst zExecutionErrorWsMessage = zExecutionWsMessageBase.extend({\n node_id: zNodeId,\n node_type: zNodeType,\n executed: z.array(zNodeId),\n exception_message: z.string(),\n exception_type: z.string(),\n traceback: z.array(z.string()),\n current_inputs: z.any(),\n current_outputs: z.any()\n})\n\nconst zProgressTextWsMessage = z.object({\n nodeId: zNodeId,\n text: z.string()\n})\n\nconst zNotificationWsMessage = z.object({\n value: z.string(),\n id: z.string().optional()\n})\nconst zTerminalSize = z.object({\n cols: z.number(),\n row: z.number()\n})\nconst zLogEntry = z.object({\n t: z.string(),\n m: z.string()\n})\nconst zLogsWsMessage = z.object({\n size: zTerminalSize.optional(),\n entries: z.array(zLogEntry)\n})\nconst zLogRawResponse = z.object({\n size: zTerminalSize,\n entries: z.array(zLogEntry)\n})\n\nconst zFeatureFlagsWsMessage = z.record(z.string(), z.any())\n\nconst zAssetDownloadWsMessage = z.object({\n task_id: z.string(),\n asset_name: z.string(),\n bytes_total: z.number(),\n bytes_downloaded: z.number(),\n progress: z.number(),\n status: z.enum(['created', 'running', 'completed', 'failed']),\n asset_id: z.string().optional(),\n error: z.string().optional()\n})\n\nexport type StatusWsMessageStatus = z.infer<typeof zStatusWsMessageStatus>\nexport type StatusWsMessage = z.infer<typeof zStatusWsMessage>\nexport type ProgressWsMessage = z.infer<typeof zProgressWsMessage>\nexport type ExecutingWsMessage = z.infer<typeof zExecutingWsMessage>\nexport type ExecutedWsMessage = z.infer<typeof zExecutedWsMessage>\nexport type ExecutionStartWsMessage = z.infer<typeof zExecutionStartWsMessage>\nexport type ExecutionSuccessWsMessage = z.infer<\n typeof zExecutionSuccessWsMessage\n>\nexport type ExecutionCachedWsMessage = z.infer<typeof zExecutionCachedWsMessage>\nexport type ExecutionInterruptedWsMessage = z.infer<\n typeof zExecutionInterruptedWsMessage\n>\nexport type ExecutionErrorWsMessage = z.infer<typeof zExecutionErrorWsMessage>\nexport type LogsWsMessage = z.infer<typeof zLogsWsMessage>\nexport type ProgressTextWsMessage = z.infer<typeof zProgressTextWsMessage>\nexport type NodeProgressState = z.infer<typeof zNodeProgressState>\nexport type ProgressStateWsMessage = z.infer<typeof zProgressStateWsMessage>\nexport type FeatureFlagsWsMessage = z.infer<typeof zFeatureFlagsWsMessage>\nexport type AssetDownloadWsMessage = z.infer<typeof zAssetDownloadWsMessage>\n// End of ws messages\n\nexport type NotificationWsMessage = z.infer<typeof zNotificationWsMessage>\n\nexport const zTaskOutput = z.record(zNodeId, zOutputs)\nexport type TaskOutput = z.infer<typeof zTaskOutput>\n\nconst zEmbeddingsResponse = z.array(z.string())\nconst zExtensionsResponse = z.array(z.string())\nconst zError = z.object({\n type: z.string(),\n message: z.string(),\n details: z.string(),\n extra_info: z\n .object({\n input_name: z.string().optional()\n })\n .passthrough()\n .optional()\n})\nconst zNodeError = z.object({\n errors: z.array(zError),\n class_type: z.string(),\n dependent_outputs: z.array(z.any())\n})\nconst zPromptResponse = z.object({\n node_errors: z.record(zNodeId, zNodeError).optional(),\n prompt_id: z.string().optional(),\n exec_info: z\n .object({\n queue_remaining: z.number().optional()\n })\n .optional(),\n error: z.union([z.string(), zError])\n})\n\nconst zDeviceStats = z.object({\n name: z.string(),\n type: z.string(),\n index: z.number(),\n vram_total: z.number(),\n vram_free: z.number(),\n torch_vram_total: z.number(),\n torch_vram_free: z.number()\n})\n\nconst zSystemStats = z.object({\n system: z.object({\n os: z.string(),\n python_version: z.string(),\n embedded_python: z.boolean(),\n comfyui_version: z.string(),\n pytorch_version: z.string(),\n required_frontend_version: z.string().optional(),\n argv: z.array(z.string()),\n ram_total: z.number(),\n ram_free: z.number(),\n // Cloud-specific fields\n cloud_version: z.string().optional(),\n comfyui_frontend_version: z.string().optional(),\n workflow_templates_version: z.string().optional()\n }),\n devices: z.array(zDeviceStats)\n})\nconst zUser = z.object({\n storage: z.enum(['server']),\n // `migrated` is only available in single-user mode.\n migrated: z.boolean().optional(),\n // `users` is only available in multi-user server mode.\n users: z.record(z.string(), z.string()).optional()\n})\nconst zUserData = z.array(z.array(z.string(), z.string()))\nconst zUserDataFullInfo = z.object({\n path: z.string(),\n size: z.number(),\n modified: z.number()\n})\nconst zBookmarkCustomization = z.object({\n icon: z.string().optional(),\n color: z.string().optional()\n})\nexport type BookmarkCustomization = z.infer<typeof zBookmarkCustomization>\n\nconst zLinkReleaseTriggerAction = z.enum(\n Object.values(LinkReleaseTriggerAction) as [string, ...string[]]\n)\n\nconst zNodeBadgeMode = z.enum(\n Object.values(NodeBadgeMode) as [string, ...string[]]\n)\n\nconst zPreviewMethod = z.enum([\n 'default',\n 'none',\n 'auto',\n 'latent2rgb',\n 'taesd'\n])\nexport type PreviewMethod = z.infer<typeof zPreviewMethod>\n\nconst zSettings = z.object({\n 'Comfy.ColorPalette': z.string(),\n 'Comfy.CustomColorPalettes': colorPalettesSchema,\n 'Comfy.Canvas.BackgroundImage': z.string().optional(),\n 'Comfy.ConfirmClear': z.boolean(),\n 'Comfy.DevMode': z.boolean(),\n 'Comfy.UI.TabBarLayout': z.enum(['Default', 'Integrated']),\n 'Comfy.Workflow.ShowMissingNodesWarning': z.boolean(),\n 'Comfy.Workflow.ShowMissingModelsWarning': z.boolean(),\n 'Comfy.Workflow.WarnBlueprintOverwrite': z.boolean(),\n 'Comfy.DisableFloatRounding': z.boolean(),\n 'Comfy.DisableSliders': z.boolean(),\n 'Comfy.DOMClippingEnabled': z.boolean(),\n 'Comfy.EditAttention.Delta': z.number(),\n 'Comfy.EnableTooltips': z.boolean(),\n 'Comfy.EnableWorkflowViewRestore': z.boolean(),\n 'Comfy.FloatRoundingPrecision': z.number(),\n 'Comfy.Graph.CanvasInfo': z.boolean(),\n 'Comfy.Graph.CanvasMenu': z.boolean(),\n 'Comfy.Graph.CtrlShiftZoom': z.boolean(),\n 'Comfy.Graph.LiveSelection': z.boolean(),\n 'Comfy.Graph.LinkMarkers': z.nativeEnum(LinkMarkerShape),\n 'Comfy.Graph.ZoomSpeed': z.number(),\n 'Comfy.Group.DoubleClickTitleToEdit': z.boolean(),\n 'Comfy.GroupSelectedNodes.Padding': z.number(),\n 'Comfy.Locale': z.string(),\n 'Comfy.NodeLibrary.Bookmarks': z.array(z.string()),\n 'Comfy.NodeLibrary.Bookmarks.V2': z.array(z.string()),\n 'Comfy.NodeLibrary.BookmarksCustomization': z.record(\n z.string(),\n zBookmarkCustomization\n ),\n 'Comfy.LinkRelease.Action': zLinkReleaseTriggerAction,\n 'Comfy.LinkRelease.ActionShift': zLinkReleaseTriggerAction,\n 'Comfy.ModelLibrary.AutoLoadAll': z.boolean(),\n 'Comfy.ModelLibrary.NameFormat': z.enum(['filename', 'title']),\n 'Comfy.NodeSearchBoxImpl.NodePreview': z.boolean(),\n 'Comfy.NodeSearchBoxImpl': z.enum(['default', 'simple']),\n 'Comfy.NodeSearchBoxImpl.ShowCategory': z.boolean(),\n 'Comfy.NodeSearchBoxImpl.ShowIdName': z.boolean(),\n 'Comfy.NodeSearchBoxImpl.ShowNodeFrequency': z.boolean(),\n 'Comfy.NodeSuggestions.number': z.number(),\n 'Comfy.Node.BypassAllLinksOnDelete': z.boolean(),\n 'Comfy.Node.Opacity': z.number(),\n 'Comfy.Node.MiddleClickRerouteNode': z.boolean(),\n 'Comfy.Node.ShowDeprecated': z.boolean(),\n 'Comfy.Node.ShowExperimental': z.boolean(),\n 'Comfy.Pointer.ClickBufferTime': z.number(),\n 'Comfy.Pointer.ClickDrift': z.number(),\n 'Comfy.Pointer.DoubleClickTime': z.number(),\n 'Comfy.PreviewFormat': z.string(),\n 'Comfy.PromptFilename': z.boolean(),\n 'Comfy.Sidebar.Location': z.enum(['left', 'right']),\n 'Comfy.Sidebar.Size': z.enum(['small', 'normal']),\n 'Comfy.Sidebar.UnifiedWidth': z.boolean(),\n 'Comfy.Sidebar.Style': z.enum(['floating', 'connected']),\n 'Comfy.SnapToGrid.GridSize': z.number(),\n 'Comfy.TextareaWidget.FontSize': z.number(),\n 'Comfy.TextareaWidget.Spellcheck': z.boolean(),\n 'Comfy.UseNewMenu': z.enum(['Disabled', 'Top']),\n 'Comfy.TreeExplorer.ItemPadding': z.number(),\n 'Comfy.Validation.Workflows': z.boolean(),\n 'Comfy.Workflow.SortNodeIdOnSave': z.boolean(),\n 'Comfy.Execution.PreviewMethod': zPreviewMethod,\n 'Comfy.Workflow.WorkflowTabsPosition': z.enum(['Sidebar', 'Topbar']),\n 'Comfy.Node.DoubleClickTitleToEdit': z.boolean(),\n 'Comfy.WidgetControlMode': z.enum(['before', 'after']),\n 'Comfy.Window.UnloadConfirmation': z.boolean(),\n 'Comfy.NodeBadge.NodeSourceBadgeMode': zNodeBadgeMode,\n 'Comfy.NodeBadge.NodeIdBadgeMode': zNodeBadgeMode,\n 'Comfy.NodeBadge.NodeLifeCycleBadgeMode': zNodeBadgeMode,\n 'Comfy.NodeBadge.ShowApiPricing': z.boolean(),\n 'Comfy.Notification.ShowVersionUpdates': z.boolean(),\n 'Comfy.QueueButton.BatchCountLimit': z.number(),\n 'Comfy.Queue.MaxHistoryItems': z.number(),\n 'Comfy.Queue.History.Expanded': z.boolean(),\n 'Comfy.Keybinding.UnsetBindings': z.array(zKeybinding),\n 'Comfy.Keybinding.NewBindings': z.array(zKeybinding),\n 'Comfy.Extension.Disabled': z.array(z.string()),\n 'Comfy.LinkRenderMode': z.number(),\n 'Comfy.Node.AutoSnapLinkToSlot': z.boolean(),\n 'Comfy.Node.SnapHighlightsNode': z.boolean(),\n 'Comfy.Server.ServerConfigValues': z.record(z.string(), z.any()),\n 'Comfy.Server.LaunchArgs': z.record(z.string(), z.string()),\n 'LiteGraph.Canvas.MaximumFps': z.number(),\n 'Comfy.Workflow.ConfirmDelete': z.boolean(),\n 'Comfy.Workflow.AutoSaveDelay': z.number(),\n 'Comfy.Workflow.AutoSave': z.enum(['off', 'after delay']),\n 'Comfy.RerouteBeta': z.boolean(),\n 'LiteGraph.Canvas.MinFontSizeForLOD': z.number(),\n 'Comfy.Canvas.SelectionToolbox': z.boolean(),\n 'LiteGraph.Node.TooltipDelay': z.number(),\n 'LiteGraph.ContextMenu.Scaling': z.boolean(),\n 'LiteGraph.Reroute.SplineOffset': z.number(),\n 'LiteGraph.Canvas.LowQualityRenderingZoomThreshold': z.number(),\n 'Comfy.Toast.DisableReconnectingToast': z.boolean(),\n 'Comfy.Workflow.Persist': z.boolean(),\n 'Comfy.TutorialCompleted': z.boolean(),\n 'Comfy.InstalledVersion': z.string().nullable(),\n 'Comfy.Node.AllowImageSizeDraw': z.boolean(),\n 'Comfy.Minimap.Visible': z.boolean(),\n 'Comfy.Minimap.NodeColors': z.boolean(),\n 'Comfy.Minimap.ShowLinks': z.boolean(),\n 'Comfy.Minimap.ShowGroups': z.boolean(),\n 'Comfy.Minimap.RenderBypassState': z.boolean(),\n 'Comfy.Minimap.RenderErrorState': z.boolean(),\n 'Comfy.Canvas.NavigationMode': z.string(),\n 'Comfy.Canvas.LeftMouseClickBehavior': z.string(),\n 'Comfy.Canvas.MouseWheelScroll': z.string(),\n 'Comfy.VueNodes.Enabled': z.boolean(),\n 'Comfy.VueNodes.AutoScaleLayout': z.boolean(),\n 'Comfy.Assets.UseAssetAPI': z.boolean(),\n 'Comfy.Queue.QPOV2': z.boolean(),\n 'Comfy-Desktop.AutoUpdate': z.boolean(),\n 'Comfy-Desktop.SendStatistics': z.boolean(),\n 'Comfy-Desktop.WindowStyle': z.string(),\n 'Comfy-Desktop.UV.PythonInstallMirror': z.string(),\n 'Comfy-Desktop.UV.PypiInstallMirror': z.string(),\n 'Comfy-Desktop.UV.TorchInstallMirror': z.string(),\n 'Comfy.MaskEditor.BrushAdjustmentSpeed': z.number(),\n 'Comfy.MaskEditor.UseDominantAxis': z.boolean(),\n 'Comfy.Load3D.ShowGrid': z.boolean(),\n 'Comfy.Load3D.BackgroundColor': z.string(),\n 'Comfy.Load3D.LightIntensity': z.number(),\n 'Comfy.Load3D.LightIntensityMaximum': z.number(),\n 'Comfy.Load3D.LightIntensityMinimum': z.number(),\n 'Comfy.Load3D.LightAdjustmentIncrement': z.number(),\n 'Comfy.Load3D.CameraType': z.enum(['perspective', 'orthographic']),\n 'Comfy.Load3D.3DViewerEnable': z.boolean(),\n 'Comfy.Load3D.PLYEngine': z.enum(['threejs', 'fastply', 'sparkjs']),\n 'Comfy.Memory.AllowManualUnload': z.boolean(),\n 'pysssss.SnapToGrid': z.boolean(),\n /** VHS setting is used for queue video preview support. */\n 'VHS.AdvancedPreviews': z.string(),\n /** Release data settings */\n 'Comfy.Release.Version': z.string(),\n 'Comfy.Release.Status': z.enum([\n 'skipped',\n 'changelog seen',\n \"what's new seen\"\n ]),\n 'Comfy.Release.Timestamp': z.number(),\n /** Template library filter settings */\n 'Comfy.Templates.SelectedModels': z.array(z.string()),\n 'Comfy.Templates.SelectedUseCases': z.array(z.string()),\n 'Comfy.Templates.SelectedRunsOn': z.array(z.string()),\n 'Comfy.Templates.SortBy': z.enum([\n 'default',\n 'recommended',\n 'popular',\n 'alphabetical',\n 'newest',\n 'vram-low-to-high',\n 'model-size-low-to-high'\n ]),\n /** Settings used for testing */\n 'test.setting': z.any(),\n 'main.sub.setting.name': z.any(),\n 'single.setting': z.any(),\n 'LiteGraph.Node.DefaultPadding': z.boolean(),\n 'LiteGraph.Pointer.TrackpadGestures': z.boolean(),\n 'Comfy.VersionCompatibility.DisableWarnings': z.boolean(),\n 'Comfy.RightSidePanel.IsOpen': z.boolean()\n})\n\nexport type EmbeddingsResponse = z.infer<typeof zEmbeddingsResponse>\nexport type ExtensionsResponse = z.infer<typeof zExtensionsResponse>\nexport type PromptResponse = z.infer<typeof zPromptResponse>\nexport type NodeError = z.infer<typeof zNodeError>\nexport type Settings = z.infer<typeof zSettings>\nexport type DeviceStats = z.infer<typeof zDeviceStats>\nexport type SystemStats = z.infer<typeof zSystemStats>\nexport type User = z.infer<typeof zUser>\nexport type UserData = z.infer<typeof zUserData>\nexport type UserDataFullInfo = z.infer<typeof zUserDataFullInfo>\nexport type TerminalSize = z.infer<typeof zTerminalSize>\nexport type LogEntry = z.infer<typeof zLogEntry>\nexport type LogsRawResponse = z.infer<typeof zLogRawResponse>\n","/**\n * @fileoverview Jobs API types - Backend job API format\n * @module platform/remote/comfyui/jobs/jobTypes\n *\n * These types represent the jobs API format returned by the backend.\n * Jobs API provides a memory-optimized alternative to history API.\n */\n\nimport { z } from 'zod'\n\nimport { resultItemType, zTaskOutput } from '@/schemas/apiSchema'\n\nconst zJobStatus = z.enum([\n 'pending',\n 'in_progress',\n 'completed',\n 'failed',\n 'cancelled'\n])\n\nconst zPreviewOutput = z.object({\n filename: z.string(),\n subfolder: z.string(),\n type: resultItemType,\n nodeId: z.string(),\n mediaType: z.string()\n})\n\n/**\n * Execution error from Jobs API.\n * Similar to ExecutionErrorWsMessage but with optional prompt_id/timestamp/executed\n * since these may not be present in stored errors or infrastructure-generated errors.\n */\nconst zExecutionError = z\n .object({\n prompt_id: z.string().optional(),\n timestamp: z.number().optional(),\n node_id: z.string(),\n node_type: z.string(),\n executed: z.array(z.string()).optional(),\n exception_message: z.string(),\n exception_type: z.string(),\n traceback: z.array(z.string()),\n current_inputs: z.unknown(),\n current_outputs: z.unknown()\n })\n .passthrough()\n\nexport type ExecutionError = z.infer<typeof zExecutionError>\n\n/**\n * Raw job from API - uses passthrough to allow extra fields\n */\nconst zRawJobListItem = z\n .object({\n id: z.string(),\n status: zJobStatus,\n create_time: z.number(),\n execution_start_time: z.number().nullable().optional(),\n execution_end_time: z.number().nullable().optional(),\n preview_output: zPreviewOutput.nullable().optional(),\n outputs_count: z.number().nullable().optional(),\n execution_error: zExecutionError.nullable().optional(),\n workflow_id: z.string().nullable().optional(),\n priority: z.number().optional()\n })\n .passthrough()\n\n/**\n * Job detail - returned by GET /api/jobs/{job_id} (detail endpoint)\n * Includes full workflow and outputs for re-execution and downloads\n */\nexport const zJobDetail = zRawJobListItem\n .extend({\n workflow: z.unknown().optional(),\n outputs: zTaskOutput.optional(),\n update_time: z.number().optional(),\n execution_status: z.unknown().optional(),\n execution_meta: z.unknown().optional()\n })\n .passthrough()\n\nconst zPaginationInfo = z.object({\n offset: z.number(),\n limit: z.number(),\n total: z.number(),\n has_more: z.boolean()\n})\n\nexport const zJobsListResponse = z.object({\n jobs: z.array(zRawJobListItem),\n pagination: zPaginationInfo\n})\n\n/** Schema for workflow container structure in job detail responses */\nexport const zWorkflowContainer = z.object({\n extra_data: z\n .object({\n extra_pnginfo: z\n .object({\n workflow: z.unknown()\n })\n .optional()\n })\n .optional()\n})\n\nexport type JobStatus = z.infer<typeof zJobStatus>\nexport type RawJobListItem = z.infer<typeof zRawJobListItem>\n/** Job list item with priority always set (server-provided or synthetic) */\nexport type JobListItem = RawJobListItem & { priority: number }\nexport type JobDetail = z.infer<typeof zJobDetail>\n\n/** Task type used in the API (queue vs history endpoints) */\nexport type APITaskType = 'queue' | 'history'\n\n/** Internal task type derived from job status for UI display */\nexport type TaskType = 'Running' | 'Pending' | 'History'\n","/**\n * @fileoverview Jobs API Fetchers\n * @module platform/remote/comfyui/jobs/fetchJobs\n *\n * Unified jobs API fetcher for history, queue, and job details.\n * All distributions use the /jobs endpoint.\n */\n\nimport type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema'\nimport { validateComfyWorkflow } from '@/platform/workflow/validation/schemas/workflowSchema'\nimport type { PromptId } from '@/schemas/apiSchema'\n\nimport type {\n JobDetail,\n JobListItem,\n JobStatus,\n RawJobListItem\n} from './jobTypes'\nimport { zJobDetail, zJobsListResponse, zWorkflowContainer } from './jobTypes'\n\ninterface FetchJobsRawResult {\n jobs: RawJobListItem[]\n total: number\n offset: number\n}\n\n/**\n * Fetches raw jobs from /jobs endpoint\n * @internal\n */\nasync function fetchJobsRaw(\n fetchApi: (url: string) => Promise<Response>,\n statuses: JobStatus[],\n maxItems: number = 200,\n offset: number = 0\n): Promise<FetchJobsRawResult> {\n const statusParam = statuses.join(',')\n const url = `/jobs?status=${statusParam}&limit=${maxItems}&offset=${offset}`\n try {\n const res = await fetchApi(url)\n if (!res.ok) {\n console.error(`[Jobs API] Failed to fetch jobs: ${res.status}`)\n return { jobs: [], total: 0, offset: 0 }\n }\n const data = zJobsListResponse.parse(await res.json())\n return { jobs: data.jobs, total: data.pagination.total, offset }\n } catch (error) {\n console.error('[Jobs API] Error fetching jobs:', error)\n return { jobs: [], total: 0, offset: 0 }\n }\n}\n\n// Large offset to ensure running/pending jobs sort above history\nconst QUEUE_PRIORITY_BASE = 1_000_000\n\n/**\n * Assigns synthetic priority to jobs.\n * Only assigns if job doesn't already have a server-provided priority.\n */\nfunction assignPriority(\n jobs: RawJobListItem[],\n basePriority: number\n): JobListItem[] {\n return jobs.map((job, index) => ({\n ...job,\n priority: job.priority ?? basePriority - index\n }))\n}\n\n/**\n * Fetches history (terminal state jobs: completed, failed, cancelled)\n * Assigns synthetic priority starting from total (lower than queue jobs).\n */\nexport async function fetchHistory(\n fetchApi: (url: string) => Promise<Response>,\n maxItems: number = 200,\n offset: number = 0\n): Promise<JobListItem[]> {\n const { jobs, total } = await fetchJobsRaw(\n fetchApi,\n ['completed', 'failed', 'cancelled'],\n maxItems,\n offset\n )\n // History gets priority based on total count (lower than queue)\n return assignPriority(jobs, total - offset)\n}\n\n/**\n * Fetches queue (in_progress + pending jobs)\n * Pending jobs get highest priority, then running jobs.\n */\nexport async function fetchQueue(\n fetchApi: (url: string) => Promise<Response>\n): Promise<{ Running: JobListItem[]; Pending: JobListItem[] }> {\n const { jobs } = await fetchJobsRaw(\n fetchApi,\n ['in_progress', 'pending'],\n 200,\n 0\n )\n\n const running = jobs.filter((j) => j.status === 'in_progress')\n const pending = jobs.filter((j) => j.status === 'pending')\n\n // Pending gets highest priority, then running\n // Both are above any history job due to QUEUE_PRIORITY_BASE\n return {\n Running: assignPriority(running, QUEUE_PRIORITY_BASE + running.length),\n Pending: assignPriority(\n pending,\n QUEUE_PRIORITY_BASE + running.length + pending.length\n )\n }\n}\n\n/**\n * Fetches full job details from /jobs/{job_id}\n */\nexport async function fetchJobDetail(\n fetchApi: (url: string) => Promise<Response>,\n promptId: PromptId\n): Promise<JobDetail | undefined> {\n try {\n const res = await fetchApi(`/jobs/${encodeURIComponent(promptId)}`)\n\n if (!res.ok) {\n console.warn(`Job not found for prompt ${promptId}`)\n return undefined\n }\n\n return zJobDetail.parse(await res.json())\n } catch (error) {\n console.error(`Failed to fetch job detail for prompt ${promptId}:`, error)\n return undefined\n }\n}\n\n/**\n * Extracts and validates workflow from job detail response.\n * The workflow is nested at: workflow.extra_data.extra_pnginfo.workflow\n *\n * Uses Zod validation via validateComfyWorkflow to ensure the workflow\n * conforms to the expected schema. Logs validation failures for debugging\n * but still returns undefined to allow graceful degradation.\n */\nexport async function extractWorkflow(\n job: JobDetail | undefined\n): Promise<ComfyWorkflowJSON | undefined> {\n const parsed = zWorkflowContainer.safeParse(job?.workflow)\n if (!parsed.success) return undefined\n\n const rawWorkflow = parsed.data.extra_data?.extra_pnginfo?.workflow\n if (!rawWorkflow) return undefined\n\n const validated = await validateComfyWorkflow(rawWorkflow, (error) => {\n console.warn('[extractWorkflow] Workflow validation failed:', error)\n })\n\n return validated ?? undefined\n}\n","import { promiseTimeout, until } from '@vueuse/core'\nimport axios from 'axios'\nimport { get } from 'es-toolkit/compat'\nimport { trimEnd } from 'es-toolkit'\n\nimport defaultClientFeatureFlags from '@/config/clientFeatureFlags.json' with { type: 'json' }\nimport type {\n ModelFile,\n ModelFolderInfo\n} from '@/platform/assets/schemas/assetSchema'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useToastStore } from '@/platform/updates/common/toastStore'\nimport type { IFuseOptions } from 'fuse.js'\nimport {\n type TemplateInfo,\n type WorkflowTemplates\n} from '@/platform/workflow/templates/types/template'\nimport type {\n ComfyApiWorkflow,\n ComfyWorkflowJSON,\n NodeId\n} from '@/platform/workflow/validation/schemas/workflowSchema'\nimport type {\n AssetDownloadWsMessage,\n EmbeddingsResponse,\n ExecutedWsMessage,\n ExecutingWsMessage,\n ExecutionCachedWsMessage,\n ExecutionErrorWsMessage,\n ExecutionInterruptedWsMessage,\n ExecutionStartWsMessage,\n ExecutionSuccessWsMessage,\n ExtensionsResponse,\n FeatureFlagsWsMessage,\n LogsRawResponse,\n LogsWsMessage,\n NotificationWsMessage,\n ProgressStateWsMessage,\n ProgressTextWsMessage,\n ProgressWsMessage,\n PromptResponse,\n Settings,\n StatusWsMessage,\n StatusWsMessageStatus,\n SystemStats,\n User,\n UserDataFullInfo,\n PreviewMethod\n} from '@/schemas/apiSchema'\nimport type {\n JobDetail,\n JobListItem\n} from '@/platform/remote/comfyui/jobs/jobTypes'\nimport type { ComfyNodeDef } from '@/schemas/nodeDefSchema'\nimport type { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\nimport type { AuthHeader } from '@/types/authTypes'\nimport type { NodeExecutionId } from '@/types/nodeIdentification'\nimport {\n fetchHistory,\n fetchJobDetail,\n fetchQueue\n} from '@/platform/remote/comfyui/jobs/fetchJobs'\n\ninterface QueuePromptRequestBody {\n client_id: string\n prompt: ComfyApiWorkflow\n partial_execution_targets?: NodeExecutionId[]\n extra_data: {\n extra_pnginfo: {\n workflow: ComfyWorkflowJSON\n }\n /**\n * The auth token for the comfy org account if the user is logged in.\n *\n * Backend node can access this token by specifying following input:\n * ```python\n @classmethod\n def INPUT_TYPES(s):\n return {\n \"hidden\": { \"auth_token\": \"AUTH_TOKEN_COMFY_ORG\"}\n }\n\n def execute(self, auth_token: str):\n print(f\"Auth token: {auth_token}\")\n * ```\n */\n auth_token_comfy_org?: string\n /**\n * The auth token for the comfy org account if the user is logged in.\n *\n * Backend node can access this token by specifying following input:\n * ```python\n * def INPUT_TYPES(s):\n * return {\n * \"hidden\": { \"api_key\": \"API_KEY_COMFY_ORG\" }\n * }\n *\n * def execute(self, api_key: str):\n * print(f\"API Key: {api_key}\")\n * ```\n */\n api_key_comfy_org?: string\n /**\n * Override the preview method for this prompt execution.\n * 'default' uses the server's CLI setting.\n */\n preview_method?: PreviewMethod\n }\n front?: boolean\n number?: number\n}\n\n/**\n * Options for queuePrompt method\n */\ninterface QueuePromptOptions {\n /**\n * Optional list of node execution IDs to execute (partial execution).\n * Each ID represents a node's position in nested subgraphs.\n * Format: Colon-separated path of node IDs (e.g., \"123:456:789\")\n */\n partialExecutionTargets?: NodeExecutionId[]\n /**\n * Override the preview method for this prompt execution.\n * 'default' uses the server's CLI setting and is not sent to backend.\n */\n previewMethod?: PreviewMethod\n}\n\n/** Dictionary of Frontend-generated API calls */\ninterface FrontendApiCalls {\n graphChanged: ComfyWorkflowJSON\n promptQueued: { number: number; batchCount: number }\n graphCleared: never\n reconnecting: never\n reconnected: never\n}\n\n/** Dictionary of calls originating from ComfyUI core */\ninterface BackendApiCalls {\n progress: ProgressWsMessage\n executing: ExecutingWsMessage\n executed: ExecutedWsMessage\n status: StatusWsMessage\n notification: NotificationWsMessage\n execution_start: ExecutionStartWsMessage\n execution_success: ExecutionSuccessWsMessage\n execution_error: ExecutionErrorWsMessage\n execution_interrupted: ExecutionInterruptedWsMessage\n execution_cached: ExecutionCachedWsMessage\n logs: LogsWsMessage\n /** Binary preview/progress data */\n b_preview: Blob\n /** Binary preview with metadata (node_id, prompt_id) */\n b_preview_with_metadata: {\n blob: Blob\n nodeId: string\n parentNodeId: string\n displayNodeId: string\n realNodeId: string\n promptId: string\n }\n progress_text: ProgressTextWsMessage\n progress_state: ProgressStateWsMessage\n feature_flags: FeatureFlagsWsMessage\n asset_download: AssetDownloadWsMessage\n}\n\n/** Dictionary of all api calls */\ninterface ApiCalls extends BackendApiCalls, FrontendApiCalls {}\n\n/** Used to create a discriminating union on type value. */\ninterface ApiMessage<T extends keyof ApiCalls> {\n type: T\n data: ApiCalls[T]\n}\n\nexport class UnauthorizedError extends Error {}\n\n/** Ensures workers get a fair shake. */\ntype Unionize<T> = T[keyof T]\n\n/**\n * Discriminated union of generic, i.e.:\n * ```ts\n * // Convert\n * type ApiMessageUnion = ApiMessage<'status' | 'executing' | ...>\n * // To\n * type ApiMessageUnion = ApiMessage<'status'> | ApiMessage<'executing'> | ...\n * ```\n */\ntype ApiMessageUnion = Unionize<{\n [Key in keyof ApiCalls]: ApiMessage<Key>\n}>\n\n/** Wraps all properties in {@link CustomEvent}. */\ntype AsCustomEvents<T> = {\n readonly [K in keyof T]: CustomEvent<T[K]>\n}\n\n/** Handles differing event and API signatures. */\ntype ApiToEventType<T = ApiCalls> = {\n [K in keyof T]: K extends 'status'\n ? StatusWsMessageStatus\n : K extends 'executing'\n ? NodeId\n : T[K]\n}\n\n/** Dictionary of types used in the detail for a custom event */\ntype ApiEventTypes = ApiToEventType<ApiCalls>\n\n/** Dictionary of API events: `[name]: CustomEvent<Type>` */\ntype ApiEvents = AsCustomEvents<ApiEventTypes>\n\n/** {@link Omit} all properties that evaluate to `never`. */\ntype NeverNever<T> = {\n [K in keyof T as T[K] extends never ? never : K]: T[K]\n}\n\n/** {@link Pick} only properties that evaluate to `never`. */\ntype PickNevers<T> = {\n [K in keyof T as T[K] extends never ? K : never]: T[K]\n}\n\n/** Keys (names) of API events that _do not_ pass a {@link CustomEvent} `detail` object. */\ntype SimpleApiEvents = keyof PickNevers<ApiEventTypes>\n/** Keys (names) of API events that pass a {@link CustomEvent} `detail` object. */\ntype ComplexApiEvents = keyof NeverNever<ApiEventTypes>\n\nexport type GlobalSubgraphData = {\n name: string\n info: { node_pack: string }\n data: string | Promise<string>\n}\n\nfunction addHeaderEntry(headers: HeadersInit, key: string, value: string) {\n if (Array.isArray(headers)) {\n headers.push([key, value])\n } else if (headers instanceof Headers) {\n headers.set(key, value)\n } else {\n headers[key] = value\n }\n}\n\n/** EventTarget typing has no generic capability. */\nexport interface ComfyApi extends EventTarget {\n addEventListener<TEvent extends keyof ApiEvents>(\n type: TEvent,\n callback: ((event: ApiEvents[TEvent]) => void) | null,\n options?: AddEventListenerOptions | boolean\n ): void\n\n removeEventListener<TEvent extends keyof ApiEvents>(\n type: TEvent,\n callback: ((event: ApiEvents[TEvent]) => void) | null,\n options?: EventListenerOptions | boolean\n ): void\n}\n\nexport class PromptExecutionError extends Error {\n response: PromptResponse\n\n constructor(response: PromptResponse) {\n super('Prompt execution failed')\n this.response = response\n }\n\n override toString() {\n let message = ''\n if (typeof this.response.error === 'string') {\n message += this.response.error\n } else if (this.response.error) {\n message +=\n this.response.error.message + ': ' + this.response.error.details\n }\n\n for (const [_, nodeError] of Object.entries(\n this.response.node_errors ?? []\n )) {\n message += '\\n' + nodeError.class_type + ':'\n for (const errorReason of nodeError.errors) {\n message += '\\n - ' + errorReason.message + ': ' + errorReason.details\n }\n }\n\n return message\n }\n}\n\nexport class ComfyApi extends EventTarget {\n #registered = new Set()\n api_host: string\n api_base: string\n /**\n * The client id from the initial session storage.\n */\n initialClientId: string | null\n /**\n * The current client id from websocket status updates.\n */\n clientId?: string\n /**\n * The current user id.\n */\n user: string\n socket: WebSocket | null = null\n\n /**\n * Cache Firebase auth store composable function.\n */\n private authStoreComposable?: typeof useFirebaseAuthStore\n\n reportedUnknownMessageTypes = new Set<string>()\n\n /**\n * Get feature flags supported by this frontend client.\n * Returns a copy to prevent external modification.\n */\n getClientFeatureFlags(): Record<string, unknown> {\n return { ...defaultClientFeatureFlags }\n }\n\n /**\n * Feature flags received from the backend server.\n */\n serverFeatureFlags: Record<string, unknown> = {}\n\n /**\n * The auth token for the comfy org account if the user is logged in.\n * This is only used for {@link queuePrompt} now. It is not directly\n * passed as parameter to the function because some custom nodes are hijacking\n * {@link queuePrompt} improperly, which causes extra parameters to be lost\n * in the function call chain.\n *\n * Ref: https://cs.comfy.org/search?q=context:global+%22api.queuePrompt+%3D%22&patternType=keyword&sm=0\n *\n * TODO: Move this field to parameter of {@link queuePrompt} once all\n * custom nodes are patched.\n */\n authToken?: string\n /**\n * The API key for the comfy org account if the user logged in via API key.\n */\n apiKey?: string\n\n constructor() {\n super()\n this.user = ''\n this.api_host = location.host\n this.api_base = isCloud\n ? ''\n : location.pathname.split('/').slice(0, -1).join('/')\n this.initialClientId = sessionStorage.getItem('clientId')\n }\n\n internalURL(route: string): string {\n return this.api_base + '/internal' + route\n }\n\n apiURL(route: string): string {\n return this.api_base + '/api' + route\n }\n\n fileURL(route: string): string {\n return this.api_base + route\n }\n\n /**\n * Gets the Firebase auth store instance using cached composable function.\n * Caches the composable function on first call, then reuses it.\n * Returns null for non-cloud distributions.\n * @returns The Firebase auth store instance, or null if not in cloud\n */\n private async getAuthStore() {\n if (isCloud) {\n if (!this.authStoreComposable) {\n const module = await import('@/stores/firebaseAuthStore')\n this.authStoreComposable = module.useFirebaseAuthStore\n }\n\n return this.authStoreComposable()\n }\n }\n\n /**\n * Waits for Firebase auth to be initialized before proceeding.\n * Includes 10-second timeout to prevent infinite hanging.\n */\n private async waitForAuthInitialization(): Promise<void> {\n if (isCloud) {\n const authStore = await this.getAuthStore()\n if (!authStore) return\n\n if (authStore.isInitialized) return\n\n try {\n await Promise.race([\n until(authStore.isInitialized),\n promiseTimeout(10000)\n ])\n } catch {\n console.warn('Firebase auth initialization timeout after 10 seconds')\n }\n }\n }\n\n async fetchApi(route: string, options?: RequestInit) {\n const headers: HeadersInit = options?.headers ?? {}\n\n if (isCloud) {\n await this.waitForAuthInitialization()\n\n // Get Firebase JWT token if user is logged in\n const getAuthHeaderIfAvailable = async (): Promise<AuthHeader | null> => {\n try {\n const authStore = await this.getAuthStore()\n return authStore ? await authStore.getAuthHeader() : null\n } catch (error) {\n console.warn('Failed to get auth header:', error)\n return null\n }\n }\n\n const authHeader = await getAuthHeaderIfAvailable()\n\n if (authHeader) {\n for (const [key, value] of Object.entries(authHeader)) {\n addHeaderEntry(headers, key, value)\n }\n }\n }\n\n addHeaderEntry(headers, 'Comfy-User', this.user)\n return fetch(this.apiURL(route), {\n cache: 'no-cache',\n ...options,\n headers\n })\n }\n\n override addEventListener<TEvent extends keyof ApiEvents>(\n type: TEvent,\n callback: ((event: ApiEvents[TEvent]) => void) | null,\n options?: AddEventListenerOptions | boolean\n ) {\n // Type assertion: strictFunctionTypes. So long as we emit events in a type-safe fashion, this is safe.\n super.addEventListener(type, callback as EventListener, options)\n this.#registered.add(type)\n }\n\n override removeEventListener<TEvent extends keyof ApiEvents>(\n type: TEvent,\n callback: ((event: ApiEvents[TEvent]) => void) | null,\n options?: EventListenerOptions | boolean\n ): void {\n super.removeEventListener(type, callback as EventListener, options)\n }\n\n /**\n * Dispatches a custom event.\n * Provides type safety for the contravariance issue with EventTarget (last checked TS 5.6).\n * @param type The type of event to emit\n * @param detail The detail property used for a custom event ({@link CustomEventInit.detail})\n */\n dispatchCustomEvent<T extends SimpleApiEvents>(type: T): boolean\n dispatchCustomEvent<T extends ComplexApiEvents>(\n type: T,\n detail: ApiEventTypes[T] | null\n ): boolean\n dispatchCustomEvent<T extends keyof ApiEventTypes>(\n type: T,\n detail?: ApiEventTypes[T]\n ): boolean {\n const event =\n detail === undefined\n ? new CustomEvent(type)\n : new CustomEvent(type, { detail })\n return super.dispatchEvent(event)\n }\n\n /** @deprecated Use {@link dispatchCustomEvent}. */\n override dispatchEvent(event: never): boolean {\n return super.dispatchEvent(event)\n }\n\n /**\n * Poll status for colab and other things that don't support websockets.\n */\n #pollQueue() {\n setInterval(async () => {\n try {\n const resp = await this.fetchApi('/prompt')\n const status = (await resp.json()) as StatusWsMessageStatus\n this.dispatchCustomEvent('status', status)\n } catch (error) {\n this.dispatchCustomEvent('status', null)\n }\n }, 1000)\n }\n\n /**\n * Creates and connects a WebSocket for realtime updates\n * @param {boolean} isReconnect If the socket is connection is a reconnect attempt\n */\n private async createSocket(isReconnect?: boolean) {\n if (this.socket) {\n return\n }\n\n let opened = false\n let existingSession = window.name\n\n // Build WebSocket URL with query parameters\n const params = new URLSearchParams()\n\n if (existingSession) {\n params.set('clientId', existingSession)\n }\n\n // Get auth token and set cloud params if available\n if (isCloud) {\n try {\n const authStore = await this.getAuthStore()\n const authToken = await authStore?.getIdToken()\n if (authToken) {\n params.set('token', authToken)\n }\n } catch (error) {\n // Continue without auth token if there's an error\n console.warn(\n 'Could not get auth token for WebSocket connection:',\n error\n )\n }\n }\n\n const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'\n const baseUrl = `${protocol}://${this.api_host}${this.api_base}/ws`\n const query = params.toString()\n const wsUrl = query ? `${baseUrl}?${query}` : baseUrl\n\n this.socket = new WebSocket(wsUrl)\n this.socket.binaryType = 'arraybuffer'\n\n this.socket.addEventListener('open', () => {\n opened = true\n\n // Send feature flags as the first message\n this.socket!.send(\n JSON.stringify({\n type: 'feature_flags',\n data: this.getClientFeatureFlags()\n })\n )\n\n if (isReconnect) {\n this.dispatchCustomEvent('reconnected')\n }\n })\n\n this.socket.addEventListener('error', () => {\n if (this.socket) this.socket.close()\n if (!isReconnect && !opened) {\n this.#pollQueue()\n }\n })\n\n this.socket.addEventListener('close', () => {\n setTimeout(async () => {\n this.socket = null\n await this.createSocket(true)\n }, 300)\n if (opened) {\n this.dispatchCustomEvent('status', null)\n this.dispatchCustomEvent('reconnecting')\n }\n })\n\n this.socket.addEventListener('message', (event) => {\n try {\n if (event.data instanceof ArrayBuffer) {\n const view = new DataView(event.data)\n const eventType = view.getUint32(0)\n\n let imageMime\n switch (eventType) {\n case 3:\n const decoder = new TextDecoder()\n const data = event.data.slice(4)\n const nodeIdLength = view.getUint32(4)\n this.dispatchCustomEvent('progress_text', {\n nodeId: decoder.decode(data.slice(4, 4 + nodeIdLength)),\n text: decoder.decode(data.slice(4 + nodeIdLength))\n })\n break\n case 1:\n const imageType = view.getUint32(4)\n const imageData = event.data.slice(8)\n switch (imageType) {\n case 2:\n imageMime = 'image/png'\n break\n case 1:\n default:\n imageMime = 'image/jpeg'\n break\n }\n const imageBlob = new Blob([imageData], {\n type: imageMime\n })\n this.dispatchCustomEvent('b_preview', imageBlob)\n break\n case 4:\n // PREVIEW_IMAGE_WITH_METADATA\n const decoder4 = new TextDecoder()\n const metadataLength = view.getUint32(4)\n const metadataBytes = event.data.slice(8, 8 + metadataLength)\n const metadata = JSON.parse(decoder4.decode(metadataBytes))\n const imageData4 = event.data.slice(8 + metadataLength)\n\n let imageMime4 = metadata.image_type\n\n const imageBlob4 = new Blob([imageData4], {\n type: imageMime4\n })\n\n // Dispatch enhanced preview event with metadata\n this.dispatchCustomEvent('b_preview_with_metadata', {\n blob: imageBlob4,\n nodeId: metadata.node_id,\n displayNodeId: metadata.display_node_id,\n parentNodeId: metadata.parent_node_id,\n realNodeId: metadata.real_node_id,\n promptId: metadata.prompt_id\n })\n\n // Also dispatch legacy b_preview for backward compatibility\n this.dispatchCustomEvent('b_preview', imageBlob4)\n break\n default:\n throw new Error(\n `Unknown binary websocket message of type ${eventType}`\n )\n }\n } else {\n const msg = JSON.parse(event.data) as ApiMessageUnion\n switch (msg.type) {\n case 'status':\n if (msg.data.sid) {\n const clientId = msg.data.sid\n this.clientId = clientId\n window.name = clientId // use window name so it isn't reused when duplicating tabs\n sessionStorage.setItem('clientId', clientId) // store in session storage so duplicate tab can load correct workflow\n }\n this.dispatchCustomEvent('status', msg.data.status ?? null)\n break\n case 'executing':\n this.dispatchCustomEvent(\n 'executing',\n msg.data.display_node || msg.data.node\n )\n break\n case 'execution_start':\n case 'execution_error':\n case 'execution_interrupted':\n case 'execution_cached':\n case 'execution_success':\n case 'progress':\n case 'progress_state':\n case 'executed':\n case 'graphChanged':\n case 'promptQueued':\n case 'logs':\n case 'b_preview':\n case 'notification':\n this.dispatchCustomEvent(msg.type, msg.data)\n break\n case 'feature_flags':\n // Store server feature flags\n this.serverFeatureFlags = msg.data\n console.log(\n 'Server feature flags received:',\n this.serverFeatureFlags\n )\n break\n default:\n if (this.#registered.has(msg.type)) {\n // Fallback for custom types - calls super direct.\n super.dispatchEvent(\n new CustomEvent(msg.type, { detail: msg.data })\n )\n } else if (!this.reportedUnknownMessageTypes.has(msg.type)) {\n this.reportedUnknownMessageTypes.add(msg.type)\n throw new Error(`Unknown message type ${msg.type}`)\n }\n }\n }\n } catch (error) {\n console.warn('Unhandled message:', event.data, error)\n }\n })\n }\n\n /**\n * Initialises sockets and realtime updates\n */\n init() {\n this.createSocket()\n }\n\n /**\n * Gets a list of extension urls\n */\n async getExtensions(): Promise<ExtensionsResponse> {\n const resp = await this.fetchApi('/extensions', { cache: 'no-store' })\n return await resp.json()\n }\n\n /**\n * Gets the available workflow templates from custom nodes.\n * @returns A map of custom_node names and associated template workflow names.\n */\n async getWorkflowTemplates(): Promise<{\n [customNodesName: string]: string[]\n }> {\n const res = await this.fetchApi('/workflow_templates')\n return await res.json()\n }\n\n /**\n * Gets the index of core workflow templates.\n * @param locale Optional locale code (e.g., 'en', 'fr', 'zh') to load localized templates\n */\n async getCoreWorkflowTemplates(\n locale?: string\n ): Promise<WorkflowTemplates[]> {\n const fileName =\n locale && locale !== 'en' ? `index.${locale}.json` : 'index.json'\n try {\n const res = await axios.get(this.fileURL(`/templates/${fileName}`))\n const contentType = res.headers['content-type']\n return contentType?.includes('application/json') ? res.data : []\n } catch (error) {\n // Fallback to default English version if localized version doesn't exist\n if (locale && locale !== 'en') {\n console.warn(\n `Localized templates for '${locale}' not found, falling back to English`\n )\n return this.getCoreWorkflowTemplates()\n }\n console.error('Error loading core workflow templates:', error)\n return []\n }\n }\n\n /**\n * Gets a list of embedding names\n */\n async getEmbeddings(): Promise<EmbeddingsResponse> {\n const resp = await this.fetchApi('/embeddings', { cache: 'no-store' })\n return await resp.json()\n }\n\n /**\n * Loads node object definitions for the graph\n * @returns The node definitions\n */\n async getNodeDefs(): Promise<Record<string, ComfyNodeDef>> {\n const resp = await this.fetchApi('/object_info', { cache: 'no-store' })\n return await resp.json()\n }\n\n /**\n * Queues a prompt to be executed\n * @param {number} number The index at which to queue the prompt, passing -1 will insert the prompt at the front of the queue\n * @param {object} data The prompt data to queue\n * @param {QueuePromptOptions} options Optional execution options\n * @throws {PromptExecutionError} If the prompt fails to execute\n */\n async queuePrompt(\n number: number,\n data: { output: ComfyApiWorkflow; workflow: ComfyWorkflowJSON },\n options?: QueuePromptOptions\n ): Promise<PromptResponse> {\n const { output: prompt, workflow } = data\n\n const body: QueuePromptRequestBody = {\n client_id: this.clientId ?? '', // TODO: Unify clientId access\n prompt,\n ...(options?.partialExecutionTargets && {\n partial_execution_targets: options.partialExecutionTargets\n }),\n extra_data: {\n auth_token_comfy_org: this.authToken,\n api_key_comfy_org: this.apiKey,\n extra_pnginfo: { workflow },\n ...(options?.previewMethod &&\n options.previewMethod !== 'default' && {\n preview_method: options.previewMethod\n })\n }\n }\n\n if (number === -1) {\n body.front = true\n } else if (number != 0) {\n body.number = number\n }\n\n const res = await this.fetchApi('/prompt', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n\n if (res.status !== 200) {\n throw new PromptExecutionError(await res.json())\n }\n\n return await res.json()\n }\n\n /**\n * Gets a list of model folder keys (eg ['checkpoints', 'loras', ...])\n * @returns The list of model folder keys\n */\n async getModelFolders(): Promise<ModelFolderInfo[]> {\n const res = await this.fetchApi(`/experiment/models`)\n if (res.status === 404) {\n return []\n }\n const folderBlacklist = ['configs', 'custom_nodes']\n return (await res.json()).filter(\n (folder: ModelFolderInfo) => !folderBlacklist.includes(folder.name)\n )\n }\n\n /**\n * Gets a list of models in the specified folder\n * @param {string} folder The folder to list models from, such as 'checkpoints'\n * @returns The list of model filenames within the specified folder\n */\n async getModels(folder: string): Promise<ModelFile[]> {\n const res = await this.fetchApi(`/experiment/models/${folder}`)\n if (res.status === 404) {\n return []\n }\n return await res.json()\n }\n\n /**\n * Gets the metadata for a model\n * @param {string} folder The folder containing the model\n * @param {string} model The model to get metadata for\n * @returns The metadata for the model\n */\n async viewMetadata(folder: string, model: string) {\n const res = await this.fetchApi(\n `/view_metadata/${folder}?filename=${encodeURIComponent(model)}`\n )\n const rawResponse = await res.text()\n if (!rawResponse) {\n return null\n }\n try {\n return JSON.parse(rawResponse)\n } catch (error) {\n console.error(\n 'Error viewing metadata',\n res.status,\n res.statusText,\n rawResponse,\n error\n )\n return null\n }\n }\n\n /**\n * Loads a list of items (queue or history)\n * @param {string} type The type of items to load, queue or history\n * @returns The items of the specified type grouped by their status\n */\n async getItems(type: 'queue' | 'history') {\n if (type === 'queue') {\n return this.getQueue()\n }\n return this.getHistory()\n }\n\n /**\n * Gets the current state of the queue\n * @returns The currently running and queued items\n */\n async getQueue(): Promise<{\n Running: JobListItem[]\n Pending: JobListItem[]\n }> {\n try {\n return await fetchQueue(this.fetchApi.bind(this))\n } catch (error) {\n console.error('Failed to fetch queue:', error)\n return { Running: [], Pending: [] }\n }\n }\n\n /**\n * Gets the prompt execution history\n * @returns Prompt history including node outputs\n */\n async getHistory(\n max_items: number = 200,\n options?: { offset?: number }\n ): Promise<JobListItem[]> {\n try {\n return await fetchHistory(\n this.fetchApi.bind(this),\n max_items,\n options?.offset\n )\n } catch (error) {\n console.error(error)\n return []\n }\n }\n\n /**\n * Gets detailed job info including outputs and workflow\n * @param jobId The job/prompt ID\n * @returns Full job details or undefined if not found\n */\n async getJobDetail(jobId: string): Promise<JobDetail | undefined> {\n return fetchJobDetail(this.fetchApi.bind(this), jobId)\n }\n\n /**\n * Gets system & device stats\n * @returns System stats such as python version, OS, per device info\n */\n async getSystemStats(): Promise<SystemStats> {\n const res = await this.fetchApi('/system_stats')\n return await res.json()\n }\n\n /**\n * Sends a POST request to the API\n * @param {*} type The endpoint to post to\n * @param {*} body Optional POST data\n */\n async #postItem(type: string, body: any) {\n try {\n await this.fetchApi('/' + type, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body ? JSON.stringify(body) : undefined\n })\n } catch (error) {\n console.error(error)\n }\n }\n\n /**\n * Deletes an item from the specified list\n * @param {string} type The type of item to delete, queue or history\n * @param {number} id The id of the item to delete\n */\n async deleteItem(type: string, id: string) {\n await this.#postItem(type, { delete: [id] })\n }\n\n /**\n * Clears the specified list\n * @param {string} type The type of list to clear, queue or history\n */\n async clearItems(type: string) {\n await this.#postItem(type, { clear: true })\n }\n\n /**\n * Interrupts the execution of the running prompt. If runningPromptId is provided,\n * it is included in the payload as a helpful hint to the backend.\n * @param {string | null} [runningPromptId] Optional Running Prompt ID to interrupt\n */\n async interrupt(runningPromptId: string | null) {\n await this.#postItem(\n 'interrupt',\n runningPromptId ? { prompt_id: runningPromptId } : undefined\n )\n }\n\n /**\n * Gets user configuration data and where data should be stored\n */\n async getUserConfig(): Promise<User> {\n return (await this.fetchApi('/users')).json()\n }\n\n /**\n * Creates a new user\n * @param { string } username\n * @returns The fetch response\n */\n createUser(username: string) {\n return this.fetchApi('/users', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ username })\n })\n }\n\n /**\n * Gets all setting values for the current user\n * @returns { Promise<string, unknown> } A dictionary of id -> value\n */\n async getSettings(): Promise<Settings> {\n const resp = await this.fetchApi('/settings')\n\n if (resp.status == 401) {\n throw new UnauthorizedError(resp.statusText)\n }\n return await resp.json()\n }\n\n /**\n * Gets a setting for the current user\n * @param { string } id The id of the setting to fetch\n * @returns { Promise<unknown> } The setting value\n */\n async getSetting(id: keyof Settings): Promise<Settings[keyof Settings]> {\n return (await this.fetchApi(`/settings/${encodeURIComponent(id)}`)).json()\n }\n\n /**\n * Stores a dictionary of settings for the current user\n */\n async storeSettings(settings: Settings) {\n return this.fetchApi(`/settings`, {\n method: 'POST',\n body: JSON.stringify(settings)\n })\n }\n\n /**\n * Stores a setting for the current user\n */\n async storeSetting(id: keyof Settings, value: Settings[keyof Settings]) {\n return this.fetchApi(`/settings/${encodeURIComponent(id)}`, {\n method: 'POST',\n body: JSON.stringify(value)\n })\n }\n\n /**\n * Gets a user data file for the current user\n */\n async getUserData(file: string, options?: RequestInit) {\n return this.fetchApi(`/userdata/${encodeURIComponent(file)}`, options)\n }\n\n /**\n * Stores a user data file for the current user\n * @param { string } file The name of the userdata file to save\n * @param { unknown } data The data to save to the file\n * @param { RequestInit & { stringify?: boolean, throwOnError?: boolean } } [options]\n * @returns { Promise<Response> }\n */\n async storeUserData(\n file: string,\n data: any,\n options: RequestInit & {\n overwrite?: boolean\n stringify?: boolean\n throwOnError?: boolean\n full_info?: boolean\n } = {\n overwrite: true,\n stringify: true,\n throwOnError: true,\n full_info: false\n }\n ): Promise<Response> {\n const resp = await this.fetchApi(\n `/userdata/${encodeURIComponent(file)}?overwrite=${options.overwrite}&full_info=${options.full_info}`,\n {\n method: 'POST',\n body: options?.stringify ? JSON.stringify(data) : data,\n ...options\n }\n )\n if (resp.status !== 200 && options.throwOnError !== false) {\n throw new Error(\n `Error storing user data file '${file}': ${resp.status} ${(await resp).statusText}`\n )\n }\n\n return resp\n }\n\n /**\n * Deletes a user data file for the current user\n * @param { string } file The name of the userdata file to delete\n */\n async deleteUserData(file: string) {\n const resp = await this.fetchApi(`/userdata/${encodeURIComponent(file)}`, {\n method: 'DELETE'\n })\n return resp\n }\n\n /**\n * Move a user data file for the current user\n * @param { string } source The userdata file to move\n * @param { string } dest The destination for the file\n */\n async moveUserData(\n source: string,\n dest: string,\n options = { overwrite: false }\n ) {\n const resp = await this.fetchApi(\n `/userdata/${encodeURIComponent(source)}/move/${encodeURIComponent(dest)}?overwrite=${options?.overwrite}`,\n {\n method: 'POST'\n }\n )\n return resp\n }\n\n async listUserDataFullInfo(dir: string): Promise<UserDataFullInfo[]> {\n const trimmedDir = trimEnd(dir, '/')\n const resp = await this.fetchApi(\n `/userdata?dir=${encodeURIComponent(trimmedDir)}&recurse=true&split=false&full_info=true`\n )\n if (resp.status === 404) return []\n if (resp.status !== 200) {\n throw new Error(\n `Error getting user data list '${trimmedDir}': ${resp.status} ${resp.statusText}`\n )\n }\n return resp.json()\n }\n\n async getGlobalSubgraphData(id: string): Promise<string> {\n const resp = await api.fetchApi('/global_subgraphs/' + id)\n if (resp.status !== 200) return ''\n const subgraph: GlobalSubgraphData = await resp.json()\n return subgraph?.data ?? ''\n }\n async getGlobalSubgraphs(): Promise<Record<string, GlobalSubgraphData>> {\n const resp = await api.fetchApi('/global_subgraphs')\n if (resp.status !== 200) return {}\n const subgraphs: Record<string, GlobalSubgraphData> = await resp.json()\n for (const [k, v] of Object.entries(subgraphs)) {\n if (!v.data) v.data = this.getGlobalSubgraphData(k)\n }\n return subgraphs\n }\n\n async getLogs(): Promise<string> {\n const url = isCloud ? this.apiURL('/logs') : this.internalURL('/logs')\n return (await axios.get(url)).data\n }\n\n async getRawLogs(): Promise<LogsRawResponse> {\n const url = isCloud\n ? this.apiURL('/logs/raw')\n : this.internalURL('/logs/raw')\n return (await axios.get(url)).data\n }\n\n async subscribeLogs(enabled: boolean): Promise<void> {\n const url = isCloud\n ? this.apiURL('/logs/subscribe')\n : this.internalURL('/logs/subscribe')\n return await axios.patch(url, {\n enabled,\n clientId: this.clientId\n })\n }\n\n async getFolderPaths(): Promise<Record<string, string[]>> {\n const response = await axios\n .get(this.internalURL('/folder_paths'))\n .catch(() => null)\n if (!response) {\n return {} // Fallback: no filesystem paths known when API unavailable\n }\n return response.data\n }\n\n /* Frees memory by unloading models and optionally freeing execution cache\n * @param {Object} options - The options object\n * @param {boolean} options.freeExecutionCache - If true, also frees execution cache\n */\n async freeMemory(options: { freeExecutionCache: boolean }) {\n try {\n let mode = ''\n if (options.freeExecutionCache) {\n mode = '{\"unload_models\": true, \"free_memory\": true}'\n } else {\n mode = '{\"unload_models\": true}'\n }\n\n const res = await this.fetchApi(`/free`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: mode\n })\n\n if (res.status === 200) {\n if (options.freeExecutionCache) {\n useToastStore().add({\n severity: 'success',\n summary: 'Models and Execution Cache have been cleared.',\n life: 3000\n })\n } else {\n useToastStore().add({\n severity: 'success',\n summary: 'Models have been unloaded.',\n life: 3000\n })\n }\n } else {\n useToastStore().add({\n severity: 'error',\n summary:\n 'Unloading of models failed. Installed ComfyUI may be an outdated version.',\n life: 5000\n })\n }\n } catch (error) {\n useToastStore().add({\n severity: 'error',\n summary: 'An error occurred while trying to unload models.',\n life: 5000\n })\n }\n }\n\n /**\n * Gets the custom nodes i18n data from the server.\n *\n * @returns The custom nodes i18n data\n */\n async getCustomNodesI18n(): Promise<Record<string, any>> {\n return (await axios.get(this.apiURL('/i18n'))).data\n }\n\n /**\n * Checks if the server supports a specific feature.\n * @param featureName The name of the feature to check (supports dot notation for nested values)\n * @returns true if the feature is supported, false otherwise\n */\n serverSupportsFeature(featureName: string): boolean {\n return get(this.serverFeatureFlags, featureName) === true\n }\n\n /**\n * Gets a server feature flag value.\n * @param featureName The name of the feature to get (supports dot notation for nested values)\n * @param defaultValue The default value if the feature is not found\n * @returns The feature value or default\n */\n getServerFeature<T = unknown>(featureName: string, defaultValue?: T): T {\n return get(this.serverFeatureFlags, featureName, defaultValue) as T\n }\n\n /**\n * Gets all server feature flags.\n * @returns Copy of all server feature flags\n */\n getServerFeatures(): Record<string, unknown> {\n return { ...this.serverFeatureFlags }\n }\n\n async getFuseOptions(): Promise<IFuseOptions<TemplateInfo> | null> {\n try {\n const res = await axios.get(\n this.fileURL('/templates/fuse_options.json'),\n {\n headers: {\n 'Content-Type': 'application/json'\n }\n }\n )\n const contentType = res.headers['content-type']\n return contentType?.includes('application/json') ? res.data : null\n } catch (error) {\n console.error('Error loading fuse options:', error)\n return null\n }\n }\n}\n\nexport const api = new ComfyApi()\n"],"file":"api-Dwq2LQIW.js"}