diff --git a/src/components/load3d/Load3DControls.vue b/src/components/load3d/Load3DControls.vue index d587d79d8e..ad7f910b19 100644 --- a/src/components/load3d/Load3DControls.vue +++ b/src/components/load3d/Load3DControls.vue @@ -88,7 +88,7 @@ import type { LightConfig, ModelConfig, SceneConfig -} from '@/extensions/core/load3d/interfaces' +} from '@/extensions/core/extensions/load3d/interfaces' const sceneConfig = defineModel('sceneConfig') const modelConfig = defineModel('modelConfig') diff --git a/src/components/load3d/controls/CameraControls.vue b/src/components/load3d/controls/CameraControls.vue index fb2d153b7f..2e9ff7d889 100644 --- a/src/components/load3d/controls/CameraControls.vue +++ b/src/components/load3d/controls/CameraControls.vue @@ -22,7 +22,7 @@ import Button from 'primevue/button' import { computed } from 'vue' import PopupSlider from '@/components/load3d/controls/PopupSlider.vue' -import type { CameraType } from '@/extensions/core/load3d/interfaces' +import type { CameraType } from '@/extensions/core/extensions/load3d/interfaces' const cameraType = defineModel('cameraType') const fov = defineModel('fov') diff --git a/src/components/load3d/controls/LightControls.vue b/src/components/load3d/controls/LightControls.vue index 1c662af859..f90381e2f2 100644 --- a/src/components/load3d/controls/LightControls.vue +++ b/src/components/load3d/controls/LightControls.vue @@ -35,7 +35,7 @@ import Button from 'primevue/button' import Slider from 'primevue/slider' import { computed, onMounted, onUnmounted, ref } from 'vue' -import type { MaterialMode } from '@/extensions/core/load3d/interfaces' +import type { MaterialMode } from '@/extensions/core/extensions/load3d/interfaces' import { useSettingStore } from '@/platform/settings/settingStore' const lightIntensity = defineModel('lightIntensity') diff --git a/src/components/load3d/controls/ModelControls.vue b/src/components/load3d/controls/ModelControls.vue index eae0092e9a..755e939687 100644 --- a/src/components/load3d/controls/ModelControls.vue +++ b/src/components/load3d/controls/ModelControls.vue @@ -68,7 +68,7 @@ import { computed, onMounted, onUnmounted, ref } from 'vue' import type { MaterialMode, UpDirection -} from '@/extensions/core/load3d/interfaces' +} from '@/extensions/core/extensions/load3d/interfaces' import { t } from '@/i18n' const materialMode = defineModel('materialMode') diff --git a/src/components/load3d/controls/SceneControls.vue b/src/components/load3d/controls/SceneControls.vue index 62c57efb8e..91d2fad9ae 100644 --- a/src/components/load3d/controls/SceneControls.vue +++ b/src/components/load3d/controls/SceneControls.vue @@ -95,7 +95,7 @@ import Button from 'primevue/button' import { computed, ref } from 'vue' import PopupSlider from '@/components/load3d/controls/PopupSlider.vue' -import type { BackgroundRenderModeType } from '@/extensions/core/load3d/interfaces' +import type { BackgroundRenderModeType } from '@/extensions/core/extensions/load3d/interfaces' const emit = defineEmits<{ (e: 'updateBackgroundImage', file: File | null): void diff --git a/src/components/load3d/controls/viewer/ViewerCameraControls.vue b/src/components/load3d/controls/viewer/ViewerCameraControls.vue index d675ab77a4..360ce3fc3e 100644 --- a/src/components/load3d/controls/viewer/ViewerCameraControls.vue +++ b/src/components/load3d/controls/viewer/ViewerCameraControls.vue @@ -31,7 +31,7 @@ import Select from 'primevue/select' import Slider from 'primevue/slider' import { computed } from 'vue' -import type { CameraType } from '@/extensions/core/load3d/interfaces' +import type { CameraType } from '@/extensions/core/extensions/load3d/interfaces' import { t } from '@/i18n' const cameras = [ diff --git a/src/components/load3d/controls/viewer/ViewerModelControls.vue b/src/components/load3d/controls/viewer/ViewerModelControls.vue index 24ba788084..962568751e 100644 --- a/src/components/load3d/controls/viewer/ViewerModelControls.vue +++ b/src/components/load3d/controls/viewer/ViewerModelControls.vue @@ -29,7 +29,7 @@ import { computed } from 'vue' import type { MaterialMode, UpDirection -} from '@/extensions/core/load3d/interfaces' +} from '@/extensions/core/extensions/load3d/interfaces' import { t } from '@/i18n' const upDirection = defineModel('upDirection') diff --git a/src/composables/useLoad3d.ts b/src/composables/useLoad3d.ts index 30cfd0cf13..bed6e0c23f 100644 --- a/src/composables/useLoad3d.ts +++ b/src/composables/useLoad3d.ts @@ -2,8 +2,8 @@ import { toRef } from '@vueuse/core' import type { MaybeRef } from '@vueuse/core' import { nextTick, ref, toRaw, watch } from 'vue' -import Load3d from '@/extensions/core/load3d/Load3d' -import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' +import Load3d from '@/extensions/core/extensions/load3d/Load3d' +import Load3dUtils from '@/extensions/core/extensions/load3d/Load3dUtils' import type { AnimationItem, CameraConfig, @@ -13,7 +13,7 @@ import type { ModelConfig, SceneConfig, UpDirection -} from '@/extensions/core/load3d/interfaces' +} from '@/extensions/core/extensions/load3d/interfaces' import { t } from '@/i18n' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import { useToastStore } from '@/platform/updates/common/toastStore' diff --git a/src/composables/useLoad3dDrag.ts b/src/composables/useLoad3dDrag.ts index d7d5ea7f03..4557e309f7 100644 --- a/src/composables/useLoad3dDrag.ts +++ b/src/composables/useLoad3dDrag.ts @@ -1,7 +1,7 @@ import { computed, ref, toValue } from 'vue' import type { MaybeRefOrGetter } from 'vue' -import { SUPPORTED_EXTENSIONS } from '@/extensions/core/load3d/interfaces' +import { SUPPORTED_EXTENSIONS } from '@/extensions/core/extensions/load3d/interfaces' import { t } from '@/i18n' import { useToastStore } from '@/platform/updates/common/toastStore' diff --git a/src/composables/useLoad3dViewer.ts b/src/composables/useLoad3dViewer.ts index 93364a8ca1..7ab9ddca11 100644 --- a/src/composables/useLoad3dViewer.ts +++ b/src/composables/useLoad3dViewer.ts @@ -1,13 +1,13 @@ import { ref, toRaw, watch } from 'vue' -import Load3d from '@/extensions/core/load3d/Load3d' -import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' +import Load3d from '@/extensions/core/extensions/load3d/Load3d' +import Load3dUtils from '@/extensions/core/extensions/load3d/Load3dUtils' import type { BackgroundRenderModeType, CameraType, MaterialMode, UpDirection -} from '@/extensions/core/load3d/interfaces' +} from '@/extensions/core/extensions/load3d/interfaces' import { t } from '@/i18n' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import { useToastStore } from '@/platform/updates/common/toastStore' diff --git a/src/extensions/core/clipspace.ts b/src/extensions/core/extensions/clipspace/index.ts similarity index 97% rename from src/extensions/core/clipspace.ts rename to src/extensions/core/extensions/clipspace/index.ts index bc644d8d76..dffa61bf84 100644 --- a/src/extensions/core/clipspace.ts +++ b/src/extensions/core/extensions/clipspace/index.ts @@ -1,6 +1,6 @@ -import { app } from '../../scripts/app' -import { ComfyApp } from '../../scripts/app' -import { $el, ComfyDialog } from '../../scripts/ui' +import { app } from '@/scripts/app' +import { ComfyApp } from '@/scripts/app' +import { $el, ComfyDialog } from '@/scripts/ui' export class ClipspaceDialog extends ComfyDialog { static items: Array< diff --git a/src/extensions/core/extensions/cloudBadges/comfy.ext.config.ts b/src/extensions/core/extensions/cloudBadges/comfy.ext.config.ts new file mode 100644 index 0000000000..7cc79f9f61 --- /dev/null +++ b/src/extensions/core/extensions/cloudBadges/comfy.ext.config.ts @@ -0,0 +1,7 @@ +import { defineComfyExtConfig } from '@/extensions/utils' + +export default defineComfyExtConfig({ + name: 'Comfy.Cloud.Badges', + activationEvents: ['*'], + comfyCloud: true, +}) diff --git a/src/extensions/core/cloudBadges.ts b/src/extensions/core/extensions/cloudBadges/index.ts similarity index 100% rename from src/extensions/core/cloudBadges.ts rename to src/extensions/core/extensions/cloudBadges/index.ts diff --git a/src/extensions/core/extensions/cloudFeedbackTopbarButton/comfy.ext.config.ts b/src/extensions/core/extensions/cloudFeedbackTopbarButton/comfy.ext.config.ts new file mode 100644 index 0000000000..6a7164e70a --- /dev/null +++ b/src/extensions/core/extensions/cloudFeedbackTopbarButton/comfy.ext.config.ts @@ -0,0 +1,7 @@ +import { defineComfyExtConfig } from '@/extensions/utils' + +export default defineComfyExtConfig({ + name: 'Comfy.Cloud.FeedbackButton', + activationEvents: ['*'], + comfyCloud: true, +}) diff --git a/src/extensions/core/cloudFeedbackTopbarButton.ts b/src/extensions/core/extensions/cloudFeedbackTopbarButton/index.ts similarity index 100% rename from src/extensions/core/cloudFeedbackTopbarButton.ts rename to src/extensions/core/extensions/cloudFeedbackTopbarButton/index.ts diff --git a/src/extensions/core/extensions/cloudRemoteConfig/comfy.ext.config.ts b/src/extensions/core/extensions/cloudRemoteConfig/comfy.ext.config.ts new file mode 100644 index 0000000000..393068a6f9 --- /dev/null +++ b/src/extensions/core/extensions/cloudRemoteConfig/comfy.ext.config.ts @@ -0,0 +1,7 @@ +import { defineComfyExtConfig } from '@/extensions/utils' + +export default defineComfyExtConfig({ + name: 'Comfy.Cloud.RemoteConfig', + activationEvents: ['*'], + comfyCloud: true, +}) diff --git a/src/extensions/core/cloudRemoteConfig.ts b/src/extensions/core/extensions/cloudRemoteConfig/index.ts similarity index 100% rename from src/extensions/core/cloudRemoteConfig.ts rename to src/extensions/core/extensions/cloudRemoteConfig/index.ts diff --git a/src/extensions/core/extensions/cloudSessionCookie/comfy.ext.config.ts b/src/extensions/core/extensions/cloudSessionCookie/comfy.ext.config.ts new file mode 100644 index 0000000000..8356d90d62 --- /dev/null +++ b/src/extensions/core/extensions/cloudSessionCookie/comfy.ext.config.ts @@ -0,0 +1,7 @@ +import { defineComfyExtConfig } from '@/extensions/utils' + +export default defineComfyExtConfig({ + name: 'Comfy.Cloud.SessionCookie', + activationEvents: ['*'], + comfyCloud: true, +}) diff --git a/src/extensions/core/cloudSessionCookie.ts b/src/extensions/core/extensions/cloudSessionCookie/index.ts similarity index 100% rename from src/extensions/core/cloudSessionCookie.ts rename to src/extensions/core/extensions/cloudSessionCookie/index.ts diff --git a/src/extensions/core/extensions/cloudSubscription/comfy.ext.config.ts b/src/extensions/core/extensions/cloudSubscription/comfy.ext.config.ts new file mode 100644 index 0000000000..7e52058127 --- /dev/null +++ b/src/extensions/core/extensions/cloudSubscription/comfy.ext.config.ts @@ -0,0 +1,9 @@ +import { defineComfyExtConfig } from '@/extensions/utils' + +export default defineComfyExtConfig({ + name: 'Comfy.Cloud.Subscription', + activationEvents: ['*'], + comfyCloud: { + subscriptionRequired: true, + }, +}) diff --git a/src/extensions/core/cloudSubscription.ts b/src/extensions/core/extensions/cloudSubscription/index.ts similarity index 100% rename from src/extensions/core/cloudSubscription.ts rename to src/extensions/core/extensions/cloudSubscription/index.ts diff --git a/src/extensions/core/contextMenuFilter.ts b/src/extensions/core/extensions/contextMenuFilter/index.ts similarity index 99% rename from src/extensions/core/contextMenuFilter.ts rename to src/extensions/core/extensions/contextMenuFilter/index.ts index 5cb0e98c58..48b6cae6d5 100644 --- a/src/extensions/core/contextMenuFilter.ts +++ b/src/extensions/core/extensions/contextMenuFilter/index.ts @@ -4,7 +4,7 @@ import { isComboWidget } from '@/lib/litegraph/src/litegraph' -import { app } from '../../scripts/app' +import { app } from '@/scripts/app' // Adds filtering to combo context menus diff --git a/src/extensions/core/dynamicPrompts.ts b/src/extensions/core/extensions/dynamicPrompts/index.ts similarity index 100% rename from src/extensions/core/dynamicPrompts.ts rename to src/extensions/core/extensions/dynamicPrompts/index.ts diff --git a/src/extensions/core/editAttention.ts b/src/extensions/core/extensions/editAttention/index.ts similarity index 99% rename from src/extensions/core/editAttention.ts rename to src/extensions/core/extensions/editAttention/index.ts index 119b277587..5c5fcb6565 100644 --- a/src/extensions/core/editAttention.ts +++ b/src/extensions/core/extensions/editAttention/index.ts @@ -1,4 +1,4 @@ -import { app } from '../../scripts/app' +import { app } from '@/scripts/app' // Allows you to edit the attention weight by holding ctrl (or cmd) and using the up/down arrow keys diff --git a/src/extensions/core/electronAdapter.ts b/src/extensions/core/extensions/electronAdapter/index.ts similarity index 100% rename from src/extensions/core/electronAdapter.ts rename to src/extensions/core/extensions/electronAdapter/index.ts diff --git a/src/extensions/core/groupNode.ts b/src/extensions/core/extensions/groupNode/index.ts similarity index 99% rename from src/extensions/core/groupNode.ts rename to src/extensions/core/extensions/groupNode/index.ts index 5b4146c2d8..76e8ff7eef 100644 --- a/src/extensions/core/groupNode.ts +++ b/src/extensions/core/extensions/groupNode/index.ts @@ -25,10 +25,10 @@ import { ExecutableGroupNodeChildDTO } from '@/utils/executableGroupNodeChildDTO import { GROUP } from '@/utils/executableGroupNodeDto' import { deserialiseAndCreate, serialise } from '@/utils/vintageClipboard' -import { api } from '../../scripts/api' -import { app } from '../../scripts/app' -import { ManageGroupDialog } from './groupNodeManage' -import { mergeIfValid } from './widgetInputs' +import { api } from '@/scripts/api' +import { app } from '@/scripts/app' +import { ManageGroupDialog } from '../groupNodeManage' +import { mergeIfValid } from '../widgetInputs' type GroupNodeWorkflowData = { external: ComfyLink[] diff --git a/src/extensions/core/groupNodeManage.css b/src/extensions/core/extensions/groupNodeManage/groupNodeManage.css similarity index 100% rename from src/extensions/core/groupNodeManage.css rename to src/extensions/core/extensions/groupNodeManage/groupNodeManage.css diff --git a/src/extensions/core/groupNodeManage.ts b/src/extensions/core/extensions/groupNodeManage/index.ts similarity index 98% rename from src/extensions/core/groupNodeManage.ts rename to src/extensions/core/extensions/groupNodeManage/index.ts index 8e52ffccc3..de763df1d3 100644 --- a/src/extensions/core/groupNodeManage.ts +++ b/src/extensions/core/extensions/groupNodeManage/index.ts @@ -6,11 +6,11 @@ import { } from '@/lib/litegraph/src/litegraph' import { useToastStore } from '@/platform/updates/common/toastStore' -import { type ComfyApp, app } from '../../scripts/app' -import { $el } from '../../scripts/ui' -import { ComfyDialog } from '../../scripts/ui/dialog' -import { DraggableList } from '../../scripts/ui/draggableList' -import { GroupNodeConfig, GroupNodeHandler } from './groupNode' +import { type ComfyApp, app } from '@/scripts/app' +import { $el } from '@/scripts/ui' +import { ComfyDialog } from '@/scripts/ui/dialog' +import { DraggableList } from '@/scripts/ui/draggableList' +import { GroupNodeConfig, GroupNodeHandler } from '../groupNode' import './groupNodeManage.css' const ORDER: symbol = Symbol() diff --git a/src/extensions/core/groupOptions.ts b/src/extensions/core/extensions/groupOptions/index.ts similarity index 99% rename from src/extensions/core/groupOptions.ts rename to src/extensions/core/extensions/groupOptions/index.ts index 7e3240bcf7..5c95b509be 100644 --- a/src/extensions/core/groupOptions.ts +++ b/src/extensions/core/extensions/groupOptions/index.ts @@ -10,7 +10,7 @@ import { import { useSettingStore } from '@/platform/settings/settingStore' import type { ComfyExtension } from '@/types/comfy' -import { app } from '../../scripts/app' +import { app } from '@/scripts/app' function setNodeMode(node: LGraphNode, mode: number) { node.mode = mode diff --git a/src/extensions/core/load3d/AnimationManager.ts b/src/extensions/core/extensions/load3d/AnimationManager.ts similarity index 98% rename from src/extensions/core/load3d/AnimationManager.ts rename to src/extensions/core/extensions/load3d/AnimationManager.ts index a451da8cdc..6b79ac789c 100644 --- a/src/extensions/core/load3d/AnimationManager.ts +++ b/src/extensions/core/extensions/load3d/AnimationManager.ts @@ -4,7 +4,7 @@ import { type AnimationItem, type AnimationManagerInterface, type EventManagerInterface -} from '@/extensions/core/load3d/interfaces' +} from './interfaces' export class AnimationManager implements AnimationManagerInterface { currentAnimation: THREE.AnimationMixer | null = null diff --git a/src/extensions/core/load3d/CameraManager.ts b/src/extensions/core/extensions/load3d/CameraManager.ts similarity index 100% rename from src/extensions/core/load3d/CameraManager.ts rename to src/extensions/core/extensions/load3d/CameraManager.ts diff --git a/src/extensions/core/load3d/ControlsManager.ts b/src/extensions/core/extensions/load3d/ControlsManager.ts similarity index 100% rename from src/extensions/core/load3d/ControlsManager.ts rename to src/extensions/core/extensions/load3d/ControlsManager.ts diff --git a/src/extensions/core/load3d/EventManager.ts b/src/extensions/core/extensions/load3d/EventManager.ts similarity index 100% rename from src/extensions/core/load3d/EventManager.ts rename to src/extensions/core/extensions/load3d/EventManager.ts diff --git a/src/extensions/core/load3d/LightingManager.ts b/src/extensions/core/extensions/load3d/LightingManager.ts similarity index 100% rename from src/extensions/core/load3d/LightingManager.ts rename to src/extensions/core/extensions/load3d/LightingManager.ts diff --git a/src/extensions/core/load3d/Load3DConfiguration.ts b/src/extensions/core/extensions/load3d/Load3DConfiguration.ts similarity index 97% rename from src/extensions/core/load3d/Load3DConfiguration.ts rename to src/extensions/core/extensions/load3d/Load3DConfiguration.ts index a914de3883..e9c363ea26 100644 --- a/src/extensions/core/load3d/Load3DConfiguration.ts +++ b/src/extensions/core/extensions/load3d/Load3DConfiguration.ts @@ -1,11 +1,11 @@ -import Load3d from '@/extensions/core/load3d/Load3d' -import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' +import Load3d from './Load3d' +import Load3dUtils from './Load3dUtils' import type { CameraConfig, LightConfig, ModelConfig, SceneConfig -} from '@/extensions/core/load3d/interfaces' +} from './interfaces' import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets' import { useSettingStore } from '@/platform/settings/settingStore' import { api } from '@/scripts/api' diff --git a/src/extensions/core/load3d/Load3d.ts b/src/extensions/core/extensions/load3d/Load3d.ts similarity index 100% rename from src/extensions/core/load3d/Load3d.ts rename to src/extensions/core/extensions/load3d/Load3d.ts diff --git a/src/extensions/core/load3d/Load3dUtils.ts b/src/extensions/core/extensions/load3d/Load3dUtils.ts similarity index 100% rename from src/extensions/core/load3d/Load3dUtils.ts rename to src/extensions/core/extensions/load3d/Load3dUtils.ts diff --git a/src/extensions/core/load3d/LoaderManager.ts b/src/extensions/core/extensions/load3d/LoaderManager.ts similarity index 100% rename from src/extensions/core/load3d/LoaderManager.ts rename to src/extensions/core/extensions/load3d/LoaderManager.ts diff --git a/src/extensions/core/load3d/ModelExporter.ts b/src/extensions/core/extensions/load3d/ModelExporter.ts similarity index 100% rename from src/extensions/core/load3d/ModelExporter.ts rename to src/extensions/core/extensions/load3d/ModelExporter.ts diff --git a/src/extensions/core/load3d/NodeStorage.ts b/src/extensions/core/extensions/load3d/NodeStorage.ts similarity index 100% rename from src/extensions/core/load3d/NodeStorage.ts rename to src/extensions/core/extensions/load3d/NodeStorage.ts diff --git a/src/extensions/core/load3d/RecordingManager.ts b/src/extensions/core/extensions/load3d/RecordingManager.ts similarity index 100% rename from src/extensions/core/load3d/RecordingManager.ts rename to src/extensions/core/extensions/load3d/RecordingManager.ts diff --git a/src/extensions/core/load3d/SceneManager.ts b/src/extensions/core/extensions/load3d/SceneManager.ts similarity index 100% rename from src/extensions/core/load3d/SceneManager.ts rename to src/extensions/core/extensions/load3d/SceneManager.ts diff --git a/src/extensions/core/load3d/SceneModelManager.ts b/src/extensions/core/extensions/load3d/SceneModelManager.ts similarity index 100% rename from src/extensions/core/load3d/SceneModelManager.ts rename to src/extensions/core/extensions/load3d/SceneModelManager.ts diff --git a/src/extensions/core/load3d/ViewHelperManager.ts b/src/extensions/core/extensions/load3d/ViewHelperManager.ts similarity index 100% rename from src/extensions/core/load3d/ViewHelperManager.ts rename to src/extensions/core/extensions/load3d/ViewHelperManager.ts diff --git a/src/extensions/core/extensions/load3d/comfy.ext.config.ts b/src/extensions/core/extensions/load3d/comfy.ext.config.ts new file mode 100644 index 0000000000..426a973f77 --- /dev/null +++ b/src/extensions/core/extensions/load3d/comfy.ext.config.ts @@ -0,0 +1,111 @@ +import { defineComfyExtConfig } from '@/extensions/utils' + +export default defineComfyExtConfig({ + name: 'Comfy.Load3D', + activationEvents: ['onWidgets:contributes', 'onCommands:contributes', 'onSettings:contributes'], + contributes: [ + { + name: 'Comfy.Preview3D', + widgets: ['PREVIEW_3D'], + }, + { + name: 'Comfy.Load3D', + widgets: ['LOAD_3D'], + settings: [ + { + id: 'Comfy.Load3D.ShowGrid', + category: ['3D', 'Scene', 'Initial Grid Visibility'], + name: 'Initial Grid Visibility', + tooltip: + 'Controls whether the grid is visible by default when a new 3D widget is created. This default can still be toggled individually for each widget after creation.', + type: 'boolean', + defaultValue: true, + experimental: true + }, + { + id: 'Comfy.Load3D.BackgroundColor', + category: ['3D', 'Scene', 'Initial Background Color'], + name: 'Initial Background Color', + tooltip: + 'Controls the default background color of the 3D scene. This setting determines the background appearance when a new 3D widget is created, but can be adjusted individually for each widget after creation.', + type: 'color', + defaultValue: '282828', + experimental: true + }, + { + id: 'Comfy.Load3D.CameraType', + category: ['3D', 'Camera', 'Initial Camera Type'], + name: 'Initial Camera Type', + tooltip: + 'Controls whether the camera is perspective or orthographic by default when a new 3D widget is created. This default can still be toggled individually for each widget after creation.', + type: 'combo', + options: ['perspective', 'orthographic'], + defaultValue: 'perspective', + experimental: true + }, + { + id: 'Comfy.Load3D.LightIntensity', + category: ['3D', 'Light', 'Initial Light Intensity'], + name: 'Initial Light Intensity', + tooltip: + 'Sets the default brightness level of lighting in the 3D scene. This value determines how intensely lights illuminate objects when a new 3D widget is created, but can be adjusted individually for each widget after creation.', + type: 'number', + defaultValue: 3, + experimental: true + }, + { + id: 'Comfy.Load3D.LightIntensityMaximum', + category: ['3D', 'Light', 'Light Intensity Maximum'], + name: 'Light Intensity Maximum', + tooltip: + 'Sets the maximum allowable light intensity value for 3D scenes. This defines the upper brightness limit that can be set when adjusting lighting in any 3D widget.', + type: 'number', + defaultValue: 10, + experimental: true + }, + { + id: 'Comfy.Load3D.LightIntensityMinimum', + category: ['3D', 'Light', 'Light Intensity Minimum'], + name: 'Light Intensity Minimum', + tooltip: + 'Sets the minimum allowable light intensity value for 3D scenes. This defines the lower brightness limit that can be set when adjusting lighting in any 3D widget.', + type: 'number', + defaultValue: 1, + experimental: true + }, + { + id: 'Comfy.Load3D.LightAdjustmentIncrement', + category: ['3D', 'Light', 'Light Adjustment Increment'], + name: 'Light Adjustment Increment', + tooltip: + 'Controls the increment size when adjusting light intensity in 3D scenes. A smaller step value allows for finer control over lighting adjustments, while a larger value results in more noticeable changes per adjustment.', + type: 'slider', + attrs: { + min: 0.1, + max: 1, + step: 0.1 + }, + defaultValue: 0.5, + experimental: true + }, + { + id: 'Comfy.Load3D.3DViewerEnable', + category: ['3D', '3DViewer', 'Enable'], + name: 'Enable 3D Viewer (Beta)', + tooltip: + 'Enables the 3D Viewer (Beta) for selected nodes. This feature allows you to visualize and interact with 3D models directly within the full size 3d viewer.', + type: 'boolean', + defaultValue: false, + experimental: true + } + ], + commands: [ + { + id: 'Comfy.3DViewer.Open3DViewer', + icon: 'pi pi-pencil', + label: 'Open 3D Viewer (Beta) for Selected Node', + } + ], + }, + ], +}) diff --git a/src/extensions/core/load3d/exportMenuHelper.ts b/src/extensions/core/extensions/load3d/exportMenuHelper.ts similarity index 97% rename from src/extensions/core/load3d/exportMenuHelper.ts rename to src/extensions/core/extensions/load3d/exportMenuHelper.ts index d87146cc3a..15aaae9f10 100644 --- a/src/extensions/core/load3d/exportMenuHelper.ts +++ b/src/extensions/core/extensions/load3d/exportMenuHelper.ts @@ -1,7 +1,7 @@ import { t } from '@/i18n' import type { IContextMenuValue } from '@/lib/litegraph/src/interfaces' import { useToastStore } from '@/platform/updates/common/toastStore' -import Load3d from '@/extensions/core/load3d/Load3d' +import Load3d from './Load3d' import { LiteGraph } from '@/lib/litegraph/src/litegraph' const EXPORT_FORMATS = [ diff --git a/src/extensions/core/load3d.ts b/src/extensions/core/extensions/load3d/index.ts similarity index 98% rename from src/extensions/core/load3d.ts rename to src/extensions/core/extensions/load3d/index.ts index 143054c65c..7487b9299c 100644 --- a/src/extensions/core/load3d.ts +++ b/src/extensions/core/extensions/load3d/index.ts @@ -3,9 +3,9 @@ import { nextTick } from 'vue' import Load3D from '@/components/load3d/Load3D.vue' import Load3DViewerContent from '@/components/load3d/Load3dViewerContent.vue' import { nodeToLoad3dMap, useLoad3d } from '@/composables/useLoad3d' -import { createExportMenuItems } from '@/extensions/core/load3d/exportMenuHelper' -import Load3DConfiguration from '@/extensions/core/load3d/Load3DConfiguration' -import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' +import { createExportMenuItems } from './exportMenuHelper' +import Load3DConfiguration from './Load3DConfiguration' +import Load3dUtils from './Load3dUtils' import { t } from '@/i18n' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import type { IContextMenuValue } from '@/lib/litegraph/src/interfaces' diff --git a/src/extensions/core/load3d/interfaces.ts b/src/extensions/core/extensions/load3d/interfaces.ts similarity index 100% rename from src/extensions/core/load3d/interfaces.ts rename to src/extensions/core/extensions/load3d/interfaces.ts diff --git a/src/extensions/core/maskeditor/CanvasHistory.ts b/src/extensions/core/extensions/maskeditor/CanvasHistory.ts similarity index 100% rename from src/extensions/core/maskeditor/CanvasHistory.ts rename to src/extensions/core/extensions/maskeditor/CanvasHistory.ts diff --git a/src/extensions/core/maskeditor/MaskEditorDialog.ts b/src/extensions/core/extensions/maskeditor/MaskEditorDialog.ts similarity index 98% rename from src/extensions/core/maskeditor/MaskEditorDialog.ts rename to src/extensions/core/extensions/maskeditor/MaskEditorDialog.ts index 1362069aa5..b9d4d85d87 100644 --- a/src/extensions/core/maskeditor/MaskEditorDialog.ts +++ b/src/extensions/core/extensions/maskeditor/MaskEditorDialog.ts @@ -1,7 +1,7 @@ import { t } from '@/i18n' -import { api } from '../../../scripts/api' -import { ComfyApp } from '../../../scripts/app' -import { $el, ComfyDialog } from '../../../scripts/ui' +import { api } from '@/scripts/api' +import { ComfyApp } from '@/scripts/app' +import { $el, ComfyDialog } from '@/scripts/ui' import { ClipspaceDialog } from '../clipspace' import { imageLayerFilenamesByTimestamp } from './utils/maskEditorLayerFilenames' import { CanvasHistory } from './CanvasHistory' diff --git a/src/extensions/core/maskeditor/constants.ts b/src/extensions/core/extensions/maskeditor/constants.ts similarity index 100% rename from src/extensions/core/maskeditor/constants.ts rename to src/extensions/core/extensions/maskeditor/constants.ts diff --git a/src/extensions/core/maskeditor.ts b/src/extensions/core/extensions/maskeditor/index.ts similarity index 93% rename from src/extensions/core/maskeditor.ts rename to src/extensions/core/extensions/maskeditor/index.ts index dd51be475b..b280deb576 100644 --- a/src/extensions/core/maskeditor.ts +++ b/src/extensions/core/extensions/maskeditor/index.ts @@ -1,13 +1,13 @@ import _ from 'es-toolkit/compat' -import { app } from '../../scripts/app' -import { ComfyApp } from '../../scripts/app' -import { ClipspaceDialog } from './clipspace' -import { MaskEditorDialog } from './maskeditor/MaskEditorDialog' -import { MaskEditorDialogOld } from './maskEditorOld' +import { app } from '@/scripts/app' +import { ComfyApp } from '@/scripts/app' +import { ClipspaceDialog } from '../clipspace' +import { MaskEditorDialog } from './MaskEditorDialog' +import { MaskEditorDialogOld } from '../../maskEditorOld' // Import styles to inject into document -import './maskeditor/styles' +import './styles' // Function to open the mask editor function openMaskEditor(): void { diff --git a/src/extensions/core/maskeditor/managers/KeyboardManager.ts b/src/extensions/core/extensions/maskeditor/managers/KeyboardManager.ts similarity index 100% rename from src/extensions/core/maskeditor/managers/KeyboardManager.ts rename to src/extensions/core/extensions/maskeditor/managers/KeyboardManager.ts diff --git a/src/extensions/core/maskeditor/managers/MessageBroker.ts b/src/extensions/core/extensions/maskeditor/managers/MessageBroker.ts similarity index 100% rename from src/extensions/core/maskeditor/managers/MessageBroker.ts rename to src/extensions/core/extensions/maskeditor/managers/MessageBroker.ts diff --git a/src/extensions/core/maskeditor/managers/PanAndZoomManager.ts b/src/extensions/core/extensions/maskeditor/managers/PanAndZoomManager.ts similarity index 100% rename from src/extensions/core/maskeditor/managers/PanAndZoomManager.ts rename to src/extensions/core/extensions/maskeditor/managers/PanAndZoomManager.ts diff --git a/src/extensions/core/maskeditor/managers/ToolManager.ts b/src/extensions/core/extensions/maskeditor/managers/ToolManager.ts similarity index 100% rename from src/extensions/core/maskeditor/managers/ToolManager.ts rename to src/extensions/core/extensions/maskeditor/managers/ToolManager.ts diff --git a/src/extensions/core/maskeditor/managers/UIManager.ts b/src/extensions/core/extensions/maskeditor/managers/UIManager.ts similarity index 100% rename from src/extensions/core/maskeditor/managers/UIManager.ts rename to src/extensions/core/extensions/maskeditor/managers/UIManager.ts diff --git a/src/extensions/core/maskeditor/managers/index.ts b/src/extensions/core/extensions/maskeditor/managers/index.ts similarity index 100% rename from src/extensions/core/maskeditor/managers/index.ts rename to src/extensions/core/extensions/maskeditor/managers/index.ts diff --git a/src/extensions/core/maskeditor/styles.ts b/src/extensions/core/extensions/maskeditor/styles.ts similarity index 100% rename from src/extensions/core/maskeditor/styles.ts rename to src/extensions/core/extensions/maskeditor/styles.ts diff --git a/src/extensions/core/maskeditor/tools/BrushTool.ts b/src/extensions/core/extensions/maskeditor/tools/BrushTool.ts similarity index 100% rename from src/extensions/core/maskeditor/tools/BrushTool.ts rename to src/extensions/core/extensions/maskeditor/tools/BrushTool.ts diff --git a/src/extensions/core/maskeditor/tools/ColorSelectTool.ts b/src/extensions/core/extensions/maskeditor/tools/ColorSelectTool.ts similarity index 100% rename from src/extensions/core/maskeditor/tools/ColorSelectTool.ts rename to src/extensions/core/extensions/maskeditor/tools/ColorSelectTool.ts diff --git a/src/extensions/core/maskeditor/tools/PaintBucketTool.ts b/src/extensions/core/extensions/maskeditor/tools/PaintBucketTool.ts similarity index 100% rename from src/extensions/core/maskeditor/tools/PaintBucketTool.ts rename to src/extensions/core/extensions/maskeditor/tools/PaintBucketTool.ts diff --git a/src/extensions/core/maskeditor/tools/index.ts b/src/extensions/core/extensions/maskeditor/tools/index.ts similarity index 100% rename from src/extensions/core/maskeditor/tools/index.ts rename to src/extensions/core/extensions/maskeditor/tools/index.ts diff --git a/src/extensions/core/maskeditor/types.ts b/src/extensions/core/extensions/maskeditor/types.ts similarity index 100% rename from src/extensions/core/maskeditor/types.ts rename to src/extensions/core/extensions/maskeditor/types.ts diff --git a/src/extensions/core/maskeditor/utils/brushCache.ts b/src/extensions/core/extensions/maskeditor/utils/brushCache.ts similarity index 100% rename from src/extensions/core/maskeditor/utils/brushCache.ts rename to src/extensions/core/extensions/maskeditor/utils/brushCache.ts diff --git a/src/extensions/core/maskeditor/utils/canvas.ts b/src/extensions/core/extensions/maskeditor/utils/canvas.ts similarity index 100% rename from src/extensions/core/maskeditor/utils/canvas.ts rename to src/extensions/core/extensions/maskeditor/utils/canvas.ts diff --git a/src/extensions/core/maskeditor/utils/clipspace.ts b/src/extensions/core/extensions/maskeditor/utils/clipspace.ts similarity index 100% rename from src/extensions/core/maskeditor/utils/clipspace.ts rename to src/extensions/core/extensions/maskeditor/utils/clipspace.ts diff --git a/src/extensions/core/maskeditor/utils/image.ts b/src/extensions/core/extensions/maskeditor/utils/image.ts similarity index 100% rename from src/extensions/core/maskeditor/utils/image.ts rename to src/extensions/core/extensions/maskeditor/utils/image.ts diff --git a/src/extensions/core/maskeditor/utils/index.ts b/src/extensions/core/extensions/maskeditor/utils/index.ts similarity index 100% rename from src/extensions/core/maskeditor/utils/index.ts rename to src/extensions/core/extensions/maskeditor/utils/index.ts diff --git a/src/extensions/core/maskeditor/utils/maskEditorLayerFilenames.ts b/src/extensions/core/extensions/maskeditor/utils/maskEditorLayerFilenames.ts similarity index 100% rename from src/extensions/core/maskeditor/utils/maskEditorLayerFilenames.ts rename to src/extensions/core/extensions/maskeditor/utils/maskEditorLayerFilenames.ts diff --git a/src/extensions/core/matchType.ts b/src/extensions/core/extensions/matchType/index.ts similarity index 100% rename from src/extensions/core/matchType.ts rename to src/extensions/core/extensions/matchType/index.ts diff --git a/src/extensions/core/nodeTemplates.ts b/src/extensions/core/extensions/nodeTemplates/index.ts similarity index 98% rename from src/extensions/core/nodeTemplates.ts rename to src/extensions/core/extensions/nodeTemplates/index.ts index c16ebed72d..92f7627ba9 100644 --- a/src/extensions/core/nodeTemplates.ts +++ b/src/extensions/core/extensions/nodeTemplates/index.ts @@ -7,10 +7,10 @@ import { useDialogService } from '@/services/dialogService' import type { ComfyExtension } from '@/types/comfy' import { deserialiseAndCreate } from '@/utils/vintageClipboard' -import { api } from '../../scripts/api' -import { app } from '../../scripts/app' -import { $el, ComfyDialog } from '../../scripts/ui' -import { GroupNodeConfig, GroupNodeHandler } from './groupNode' +import { api } from '@/scripts/api' +import { app } from '@/scripts/app' +import { $el, ComfyDialog } from '@/scripts/ui' +import { GroupNodeConfig, GroupNodeHandler } from '../groupNode' // Adds the ability to save and add multiple nodes as a template // To save: diff --git a/src/extensions/core/noteNode.ts b/src/extensions/core/extensions/noteNode/index.ts similarity index 95% rename from src/extensions/core/noteNode.ts rename to src/extensions/core/extensions/noteNode/index.ts index 8b2d9ed992..5e5af5ca5c 100644 --- a/src/extensions/core/noteNode.ts +++ b/src/extensions/core/extensions/noteNode/index.ts @@ -1,8 +1,8 @@ import { LGraphCanvas, LiteGraph } from '@/lib/litegraph/src/litegraph' import { LGraphNode } from '@/lib/litegraph/src/litegraph' -import { app } from '../../scripts/app' -import { ComfyWidgets } from '../../scripts/widgets' +import { app } from '@/scripts/app' +import { ComfyWidgets } from '@/scripts/widgets' // Node that add notes to your project diff --git a/src/extensions/core/previewAny.ts b/src/extensions/core/extensions/previewAny/index.ts similarity index 100% rename from src/extensions/core/previewAny.ts rename to src/extensions/core/extensions/previewAny/index.ts diff --git a/src/extensions/core/rerouteNode.ts b/src/extensions/core/extensions/rerouteNode/index.ts similarity index 99% rename from src/extensions/core/rerouteNode.ts rename to src/extensions/core/extensions/rerouteNode/index.ts index 44c3f3150f..f597b7a78c 100644 --- a/src/extensions/core/rerouteNode.ts +++ b/src/extensions/core/extensions/rerouteNode/index.ts @@ -6,8 +6,8 @@ import { } from '@/lib/litegraph/src/litegraph' import type { ISlotType } from '@/lib/litegraph/src/interfaces' -import { app } from '../../scripts/app' -import { getWidgetConfig, mergeIfValid, setWidgetConfig } from './widgetInputs' +import { app } from '@/scripts/app' +import { getWidgetConfig, mergeIfValid, setWidgetConfig } from '../widgetInputs' // Node that allows you to redirect connections for cleaner graphs diff --git a/src/extensions/core/saveImageExtraOutput.ts b/src/extensions/core/extensions/saveImageExtraOutput/index.ts similarity index 97% rename from src/extensions/core/saveImageExtraOutput.ts rename to src/extensions/core/extensions/saveImageExtraOutput/index.ts index f216f31a7d..313bac4381 100644 --- a/src/extensions/core/saveImageExtraOutput.ts +++ b/src/extensions/core/extensions/saveImageExtraOutput/index.ts @@ -1,6 +1,6 @@ import { applyTextReplacements } from '@/utils/searchAndReplace' -import { app } from '../../scripts/app' +import { app } from '@/scripts/app' const saveNodeTypes = new Set([ 'SaveImage', diff --git a/src/extensions/core/extensions/saveMesh/comfy.ext.config.ts b/src/extensions/core/extensions/saveMesh/comfy.ext.config.ts new file mode 100644 index 0000000000..aea78abfc0 --- /dev/null +++ b/src/extensions/core/extensions/saveMesh/comfy.ext.config.ts @@ -0,0 +1,12 @@ +import { defineComfyExtConfig } from '@/extensions/utils' + +export default defineComfyExtConfig({ + name: 'Comfy.SaveGLB', + activationEvents: ['onWidgets:contributes', 'onCommands:contributes', 'onSettings:contributes'], + contributes: [ + { + name: 'Comfy.SaveGLB', + widgets: ['SAVE_GLB'], + }, + ], +}) diff --git a/src/extensions/core/saveMesh.ts b/src/extensions/core/extensions/saveMesh/index.ts similarity index 93% rename from src/extensions/core/saveMesh.ts rename to src/extensions/core/extensions/saveMesh/index.ts index 0955fe4cc5..d16f4bcc14 100644 --- a/src/extensions/core/saveMesh.ts +++ b/src/extensions/core/extensions/saveMesh/index.ts @@ -2,8 +2,8 @@ import { nextTick } from 'vue' import Load3D from '@/components/load3d/Load3D.vue' import { useLoad3d } from '@/composables/useLoad3d' -import { createExportMenuItems } from '@/extensions/core/load3d/exportMenuHelper' -import Load3DConfiguration from '@/extensions/core/load3d/Load3DConfiguration' +import { createExportMenuItems } from '../load3d/exportMenuHelper' +import Load3DConfiguration from '../load3d/Load3DConfiguration' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import type { IContextMenuValue } from '@/lib/litegraph/src/interfaces' import { type CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' diff --git a/src/extensions/core/selectionBorder.ts b/src/extensions/core/extensions/selectionBorder/index.ts similarity index 100% rename from src/extensions/core/selectionBorder.ts rename to src/extensions/core/extensions/selectionBorder/index.ts diff --git a/src/extensions/core/simpleTouchSupport.ts b/src/extensions/core/extensions/simpleTouchSupport/index.ts similarity index 99% rename from src/extensions/core/simpleTouchSupport.ts rename to src/extensions/core/extensions/simpleTouchSupport/index.ts index c129711ec8..742d247352 100644 --- a/src/extensions/core/simpleTouchSupport.ts +++ b/src/extensions/core/extensions/simpleTouchSupport/index.ts @@ -1,6 +1,6 @@ import { LGraphCanvas, LiteGraph } from '@/lib/litegraph/src/litegraph' -import { app } from '../../scripts/app' +import { app } from '@/scripts/app' let touchZooming = false let touchCount = 0 diff --git a/src/extensions/core/slotDefaults.ts b/src/extensions/core/extensions/slotDefaults/index.ts similarity index 97% rename from src/extensions/core/slotDefaults.ts rename to src/extensions/core/extensions/slotDefaults/index.ts index f7ca293a44..63d91f70ea 100644 --- a/src/extensions/core/slotDefaults.ts +++ b/src/extensions/core/extensions/slotDefaults/index.ts @@ -1,7 +1,7 @@ import { LiteGraph } from '@/lib/litegraph/src/litegraph' -import { app } from '../../scripts/app' -import { ComfyWidgets } from '../../scripts/widgets' +import { app } from '@/scripts/app' +import { ComfyWidgets } from '@/scripts/widgets' // Adds defaults for quickly adding nodes with middle click on the input/output diff --git a/src/extensions/core/uploadAudio.ts b/src/extensions/core/extensions/uploadAudio/index.ts similarity index 99% rename from src/extensions/core/uploadAudio.ts rename to src/extensions/core/extensions/uploadAudio/index.ts index dfefc88a0f..a6523b6e68 100644 --- a/src/extensions/core/uploadAudio.ts +++ b/src/extensions/core/extensions/uploadAudio/index.ts @@ -21,8 +21,8 @@ import { useAudioService } from '@/services/audioService' import { type NodeLocatorId } from '@/types' import { getNodeByLocatorId } from '@/utils/graphTraversalUtil' -import { api } from '../../scripts/api' -import { app } from '../../scripts/app' +import { api } from '@/scripts/api' +import { app } from '@/scripts/app' async function uploadFile( audioWidget: IStringWidget, diff --git a/src/extensions/core/uploadImage.ts b/src/extensions/core/extensions/uploadImage/index.ts similarity index 96% rename from src/extensions/core/uploadImage.ts rename to src/extensions/core/extensions/uploadImage/index.ts index 4cb910dae1..9849d393f7 100644 --- a/src/extensions/core/uploadImage.ts +++ b/src/extensions/core/extensions/uploadImage/index.ts @@ -4,7 +4,7 @@ import { isComboInputSpecV1 } from '@/schemas/nodeDefSchema' -import { app } from '../../scripts/app' +import { app } from '@/scripts/app' // Adds an upload button to the nodes diff --git a/src/extensions/core/webcamCapture.ts b/src/extensions/core/extensions/webcamCapture/index.ts similarity index 98% rename from src/extensions/core/webcamCapture.ts rename to src/extensions/core/extensions/webcamCapture/index.ts index f429ddda49..fee4562a59 100644 --- a/src/extensions/core/webcamCapture.ts +++ b/src/extensions/core/extensions/webcamCapture/index.ts @@ -1,8 +1,8 @@ import { t } from '@/i18n' import { useToastStore } from '@/platform/updates/common/toastStore' -import { api } from '../../scripts/api' -import { app } from '../../scripts/app' +import { api } from '@/scripts/api' +import { app } from '@/scripts/app' const WEBCAM_READY = Symbol() diff --git a/src/extensions/core/widgetInputs.ts b/src/extensions/core/extensions/widgetInputs/index.ts similarity index 100% rename from src/extensions/core/widgetInputs.ts rename to src/extensions/core/extensions/widgetInputs/index.ts diff --git a/src/extensions/core/index.ts b/src/extensions/core/index.ts index 4171dce89d..c00d6a21d4 100644 --- a/src/extensions/core/index.ts +++ b/src/extensions/core/index.ts @@ -1,38 +1,16 @@ -import { isCloud } from '@/platform/distribution/types' +import { dispatchComfyExtensions } from '../dispatch' +import type { ComfyExtensionConfigs, ComfyExtensionEntrance } from '../types' -import './clipspace' -import './contextMenuFilter' -import './dynamicPrompts' -import './editAttention' -import './electronAdapter' -import './groupNode' -import './groupNodeManage' -import './groupOptions' -import './load3d' -import './maskeditor' -import './matchType' -import './nodeTemplates' -import './noteNode' -import './previewAny' -import './rerouteNode' -import './saveImageExtraOutput' -import './saveMesh' -import './selectionBorder' -import './simpleTouchSupport' -import './slotDefaults' -import './uploadAudio' -import './uploadImage' -import './webcamCapture' -import './widgetInputs' +export async function registerExtensions() { + console.log('importExtensions running...') -// Cloud-only extensions - tree-shaken in OSS builds -if (isCloud) { - await import('./cloudRemoteConfig') - await import('./cloudBadges') - await import('./cloudSessionCookie') - await import('./cloudFeedbackTopbarButton') + const extConfigs = import.meta.glob(`./extensions/*/comfy.ext.config.ts`, { + // Since each config is small, we only import the default export and use eager mode for better tree-shaking and performance. + import: 'default', + eager: true, + }) as ComfyExtensionConfigs + const extensionEntrance = import.meta.glob(`./extensions/*/index.ts`) as ComfyExtensionEntrance - if (window.__CONFIG__?.subscription_required) { - await import('./cloudSubscription') - } + dispatchComfyExtensions({ configs: extConfigs, entrance: extensionEntrance }) } + diff --git a/src/extensions/core/maskEditorOld.ts b/src/extensions/core/maskEditorOld.ts index e34c3f225a..b849ebaa19 100644 --- a/src/extensions/core/maskEditorOld.ts +++ b/src/extensions/core/maskEditorOld.ts @@ -2,7 +2,7 @@ import { api } from '../../scripts/api' import { app } from '../../scripts/app' import { ComfyApp } from '../../scripts/app' import { $el, ComfyDialog } from '../../scripts/ui' -import { ClipspaceDialog } from './clipspace' +import { ClipspaceDialog } from './extensions/clipspace' // Helper function to convert a data URL to a Blob object // @ts-expect-error fixme ts strict error diff --git a/src/extensions/dispatch.ts b/src/extensions/dispatch.ts new file mode 100644 index 0000000000..efd2166eea --- /dev/null +++ b/src/extensions/dispatch.ts @@ -0,0 +1,96 @@ +import { isCloud } from '@/platform/distribution/types' +import type { + ComfyExtensionConfigs, + ComfyExtensionEntrance, + ComfyExtensionLoadContext, + StaticComfyCommand, + StaticComfyKeybinding, + StaticComfyMenuCommandGroup, + StaticComfySettingParams +} from './types' +import { + defineProcessQueue, + formatExtensions, + normalizationActivationEvents +} from './utils' + +const extLoadContext: ComfyExtensionLoadContext = { + get isCloud() { + return isCloud + }, + get subscriptionRequired() { + return !!window.__CONFIG__?.subscription_required + } +} + +export async function dispatchComfyExtensions(options: { + configs: ComfyExtensionConfigs + entrance: ComfyExtensionEntrance +}) { + const { configs, entrance } = options + const extensions = formatExtensions(entrance, configs) + for (const extension of Object.values(extensions)) { + const activationEvents = normalizationActivationEvents( + extLoadContext, + extension.config + ) + activationEvents.forEach((event) => + onceExtImportEvent(event, async () => void (await extension.entry())) + ) + + let contributes = extension.config?.contributes + if (contributes && !Array.isArray(contributes)) contributes = [contributes] + if (contributes && contributes.length) { + for (const contribute of contributes) { + const { settings, commands, keybindings, menuCommands } = contribute + if (settings) pushExtensionSettings(settings) + if (commands) pushExtensionCommands(commands) + if (keybindings) pushExtensionKeybindings(keybindings) + if (menuCommands) pushExtensionMenuCommands(menuCommands) + } + } + } +} + +type EventCallback = (ctx: { event: string }) => void | Promise + +const eventMap = new Map>() + +export async function importExtensionsByEvent(event: string) { + const callbacks = eventMap.get(event) + if (!callbacks) return + eventMap.delete(event) + await Promise.all([...callbacks].map((cb) => cb({ event }))) +} + +export function extentionsImportEventHas(event: string) { + return eventMap.has(event) +} + +function onceExtImportEvent(event: string, callback: EventCallback) { + if (eventMap.has(event)) { + eventMap.get(event)!.add(callback) + } else { + eventMap.set(event, new Set([callback])) + } +} + +const { process: _processExtensionSettings, push: pushExtensionSettings } = + defineProcessQueue() +export const processExtensionSettings = _processExtensionSettings + +const { process: _processExtensionCommands, push: pushExtensionCommands } = + defineProcessQueue() +export const processExtensionCommands = _processExtensionCommands + +const { + process: _processExtensionMenuCommands, + push: pushExtensionMenuCommands +} = defineProcessQueue() +export const processExtensionMenuCommands = _processExtensionMenuCommands + +const { + process: _processExtensionKeybindings, + push: pushExtensionKeybindings +} = defineProcessQueue() +export const processExtensionKeybindings = _processExtensionKeybindings diff --git a/src/extensions/types.ts b/src/extensions/types.ts new file mode 100644 index 0000000000..bb5ffde4c5 --- /dev/null +++ b/src/extensions/types.ts @@ -0,0 +1,75 @@ +import type { ComfyExtension } from '@/types' + +// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type +type ExcludeFunctions = T extends Function ? never : T +type StaticOnly = { + // eslint-disable-next-line prettier/prettier + [K in keyof T as ExcludeFunctions extends never + ? never + : K]: ExcludeFunctions +} + +export type StaticComfyCommand = StaticOnly< + NonNullable[number] +> +export type StaticComfySettingParams = StaticOnly< + NonNullable[number] +> +export type StaticComfyKeybinding = StaticOnly< + NonNullable[number] +> +export type StaticComfyMenuCommandGroup = StaticOnly< + NonNullable[number] +> + +export type ComfyExtensionActivationEvent = + | '*' + | 'onWidgets:contributes' + | 'onCommands:contributes' + | 'onSettings:contributes' + +type ComfyExtensionContributes = { + name: string + widgets?: string[] + commands?: StaticComfyCommand[] + menuCommands?: StaticComfyMenuCommandGroup[] + settings?: StaticComfySettingParams[] + keybindings?: StaticComfyKeybinding[] +} + +export interface ComfyExtensionConfig { + name?: string + + activationEvents: ComfyExtensionActivationEvent[] + + contributes?: ComfyExtensionContributes | ComfyExtensionContributes[] + + comfyCloud?: + | boolean + | { + subscriptionRequired: boolean + } +} +export type ComfyExtensionConfigs = Record< + string, + ComfyExtensionConfig | undefined +> + +export interface ComfyExtensionLoadContext { + readonly isCloud: boolean + readonly subscriptionRequired: boolean +} + +export type ComfyExtensionEntry = () => Promise> +export type ComfyExtensionEntrance = Record< + string, + ComfyExtensionEntry | undefined +> + +export interface ComfyExtensionPackage { + name?: string + path?: string + config?: ComfyExtensionConfig + entry: ComfyExtensionEntry +} +export type ComfyExtensionPackages = Record diff --git a/src/extensions/utils.ts b/src/extensions/utils.ts new file mode 100644 index 0000000000..d6539a9aec --- /dev/null +++ b/src/extensions/utils.ts @@ -0,0 +1,148 @@ +import type { + ComfyExtensionConfig, + ComfyExtensionConfigs, + ComfyExtensionEntrance, + ComfyExtensionLoadContext, + ComfyExtensionPackages +} from './types' + +export function defineComfyExtConfig( + config: ComfyExtensionConfig +): ComfyExtensionConfig { + return config +} + +export function formatExtensions( + entrance: ComfyExtensionEntrance, + configs: ComfyExtensionConfigs +): ComfyExtensionPackages { + const pkgs: ComfyExtensionPackages = {} + for (const [entryPath, entry] of Object.entries(entrance)) { + const pathArr = entryPath.split('/') + const name = pathArr.at(-2)! + const path = pathArr.slice(0, -1).join('/') + if (!name) { + console.error(`Extension`, path, `has no name`) + continue + } + if (!entry) { + console.error(`Extension`, path, `has no entrance`) + continue + } + + const config = configs[`${path}/comfy.ext.config.ts`] + // if (!config) { + // console.warn(`Extension`, path, `has no config`) + // } + pkgs[name] = { name, path, config, entry } + } + return pkgs +} + +export function normalizationActivationEvents( + ctx: ComfyExtensionLoadContext, + config: ComfyExtensionConfig | undefined +): string[] { + if (!config) return ['*'] + + if (!checkAboutCloud(ctx, config)) return [] + + const { activationEvents, contributes: _contributes } = config + + if (activationEvents.includes('*')) return ['*'] + + const contributes = _contributes + ? Array.isArray(_contributes) + ? _contributes + : [_contributes] + : [] + + const events: string[] = [] + + if (activationEvents.includes('onCommands:contributes')) { + for (const contribute of contributes) { + if (contribute.commands) { + for (const command of contribute.commands) { + events.push(`onCommands:${command}`) + } + } + } + } + + if (activationEvents.includes('onSettings:contributes')) { + for (const contribute of contributes) { + if (contribute.settings) { + for (const setting of contribute.settings) { + events.push(`onSettings:${setting.id}`) + } + } + } + } + + if (activationEvents.includes('onWidgets:contributes')) { + for (const contribute of contributes) { + if (contribute.widgets) { + for (const widget of contribute.widgets) { + events.push(`onWidgets:${widget}`) + } + } + } + } + + return events +} + +function checkAboutCloud( + ctx: ComfyExtensionLoadContext, + extConfig: ComfyExtensionConfig +): boolean { + // Cloud Only Extension + const { comfyCloud } = extConfig + if (comfyCloud) { + if (!ctx.isCloud) return false + if (comfyCloud === true) return true + return comfyCloud.subscriptionRequired && ctx.subscriptionRequired + } + + // Default Extension -> Load Extension + return true +} + +/** + * Defines a queue processing function for handling elements in a queue. + * When elements are present in the queue, all existing elements are automatically retrieved + * and passed to the callback function for processing. + * Use the process method to register a callback function, and use the push method to add elements to the queue. + * @returns Returns an object containing process and push methods + */ +export function defineProcessQueue(): { + process: (worker: (items: T[]) => void) => void + push: (items: T[]) => void +} { + let worker: ((items: T[]) => void) | undefined = undefined + const items: T[] = [] + function push(newItems: T[]) { + items.push(...newItems) + consume() + } + function process(newWorker: (items: T[]) => void) { + if (worker) { + throw new Error('queue worker already registered') + } + worker = newWorker + consume() + } + + function consume() { + if (worker !== undefined && items.length > 0) { + const itemsToProcess = items.slice() + items.length = 0 + worker(itemsToProcess) + } + } + + return { + process, + push + } +} diff --git a/src/platform/settings/settingStore.ts b/src/platform/settings/settingStore.ts index 5a1573efbf..6629455f20 100644 --- a/src/platform/settings/settingStore.ts +++ b/src/platform/settings/settingStore.ts @@ -8,6 +8,8 @@ import type { Settings } from '@/schemas/apiSchema' import { api } from '@/scripts/api' import { app } from '@/scripts/app' import type { TreeNode } from '@/types/treeExplorerTypes' +import { processExtensionSettings } from '@/extensions/dispatch' +import { useErrorHandling } from '@/composables/useErrorHandling' export const getSettingInfo = (setting: SettingParams) => { const parts = setting.category || setting.id.split('.') @@ -46,6 +48,14 @@ export const useSettingStore = defineStore('setting', () => { const settingValues = ref>({}) const settingsById = ref>({}) + const { wrapWithErrorHandling } = useErrorHandling() + processExtensionSettings((settings) => { + const _addSetting = wrapWithErrorHandling(addSetting) + for (const setting of settings) { + _addSetting(setting) + } + }) + /** * Check if a setting's value exists, i.e. if the user has set it manually. * @param key - The key of the setting to check. diff --git a/src/renderer/utils/nodeTypeGuards.ts b/src/renderer/utils/nodeTypeGuards.ts index 9a13edfb57..619c1a4f40 100644 --- a/src/renderer/utils/nodeTypeGuards.ts +++ b/src/renderer/utils/nodeTypeGuards.ts @@ -1,4 +1,4 @@ -import type { PrimitiveNode } from '@/extensions/core/widgetInputs' +import type { PrimitiveNode } from '@/extensions/core/extensions/widgetInputs' import type { LGraphNode } from '@/lib/litegraph/src/litegraph' export const isPrimitiveNode = ( diff --git a/src/services/extensionService.ts b/src/services/extensionService.ts index 75b159e11e..ed705ecf99 100644 --- a/src/services/extensionService.ts +++ b/src/services/extensionService.ts @@ -12,6 +12,7 @@ import { useWidgetStore } from '@/stores/widgetStore' import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore' import type { ComfyExtension } from '@/types/comfy' import type { AuthUserInfo } from '@/types/authTypes' +import { importExtensionsByEvent } from '@/extensions/dispatch' export const useExtensionService = () => { const extensionStore = useExtensionStore() @@ -35,7 +36,12 @@ export const useExtensionService = () => { // Need to load core extensions first as some custom extensions // may depend on them. - await import('../extensions/core/index') + const { registerExtensions } = await import('../extensions/core/index') + await registerExtensions() + + // Import Immediately Loaded Extensions + await importExtensionsByEvent('*') + extensionStore.captureCoreExtensions() await Promise.all( extensions diff --git a/src/services/litegraphService.ts b/src/services/litegraphService.ts index 35562533b2..074226a3de 100644 --- a/src/services/litegraphService.ts +++ b/src/services/litegraphService.ts @@ -58,10 +58,44 @@ import { import { getOrderedInputSpecs } from '@/workbench/utils/nodeDefOrderingUtil' import { useExtensionService } from './extensionService' +import { + extentionsImportEventHas, + importExtensionsByEvent +} from '@/extensions/dispatch' export const CONFIG = Symbol() export const GET_CONFIG = Symbol() +function addInputsAndimportWidgetsAsNeeded(options: { + orderedInputSpecs: InputSpec[] + addInputSocket: (inputSpec: InputSpec) => void + addInputWidget: (inputSpec: InputSpec) => void +}) { + const { orderedInputSpecs, addInputSocket, addInputWidget } = options + const awaitedInputSpecs: InputSpec[] = [] + const syncInputSpecs: InputSpec[] = [] + const importJobs: Promise[] = [] + for (const inputSpec of orderedInputSpecs) { + const widgetType = inputSpec.widgetType ?? inputSpec.type + if (extentionsImportEventHas(`onWidgets:${widgetType}`)) { + importJobs.push(importExtensionsByEvent(`onWidgets:${widgetType}`)) + awaitedInputSpecs.push(inputSpec) + } else { + syncInputSpecs.push(inputSpec) + } + } + + ;(async () => { + await Promise.all(importJobs) + for (const inputSpec of awaitedInputSpecs) addInputSocket(inputSpec) + for (const inputSpec of awaitedInputSpecs) addInputWidget(inputSpec) + })() + + // Create sockets and widgets in the determined order + for (const inputSpec of syncInputSpecs) addInputSocket(inputSpec) + for (const inputSpec of syncInputSpecs) addInputWidget(inputSpec) +} + /** * Service that augments litegraph with ComfyUI specific functionality. */ @@ -244,12 +278,11 @@ export const useLitegraphService = () => { // Use input_order if available to ensure consistent widget ordering const nodeDefImpl = ComfyNode.nodeData as ComfyNodeDefImpl const orderedInputSpecs = getOrderedInputSpecs(nodeDefImpl, inputs) - - // Create sockets and widgets in the determined order - for (const inputSpec of orderedInputSpecs) - this.#addInputSocket(inputSpec) - for (const inputSpec of orderedInputSpecs) - this.#addInputWidget(inputSpec) + addInputsAndimportWidgetsAsNeeded({ + orderedInputSpecs, + addInputSocket: this.#addInputSocket.bind(this), + addInputWidget: this.#addInputWidget.bind(this) + }) } /** @@ -521,11 +554,11 @@ export const useLitegraphService = () => { const nodeDefImpl = ComfyNode.nodeData as ComfyNodeDefImpl const orderedInputSpecs = getOrderedInputSpecs(nodeDefImpl, inputs) - // Create sockets and widgets in the determined order - for (const inputSpec of orderedInputSpecs) - this.#addInputSocket(inputSpec) - for (const inputSpec of orderedInputSpecs) - this.#addInputWidget(inputSpec) + addInputsAndimportWidgetsAsNeeded({ + orderedInputSpecs, + addInputSocket: this.#addInputSocket.bind(this), + addInputWidget: this.#addInputWidget.bind(this) + }) } /** diff --git a/src/services/load3dService.ts b/src/services/load3dService.ts index e910f4b4d4..d7be9aa742 100644 --- a/src/services/load3dService.ts +++ b/src/services/load3dService.ts @@ -2,7 +2,7 @@ import { toRaw } from 'vue' import { nodeToLoad3dMap } from '@/composables/useLoad3d' import { useLoad3dViewer } from '@/composables/useLoad3dViewer' -import type Load3d from '@/extensions/core/load3d/Load3d' +import type Load3d from '@/extensions/core/extensions/load3d/Load3d' import type { LGraphNode } from '@/lib/litegraph/src/litegraph' import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema' diff --git a/src/stores/keybindingStore.ts b/src/stores/keybindingStore.ts index 76a21120cc..5973d471b9 100644 --- a/src/stores/keybindingStore.ts +++ b/src/stores/keybindingStore.ts @@ -5,6 +5,8 @@ import { computed, ref, toRaw } from 'vue' import { RESERVED_BY_TEXT_INPUT } from '@/constants/reservedKeyCombos' import type { KeyCombo, Keybinding } from '@/schemas/keyBindingSchema' +import { processExtensionKeybindings } from '@/extensions/dispatch' +import { useErrorHandling } from '@/composables/useErrorHandling' export class KeybindingImpl implements Keybinding { commandId: string @@ -171,6 +173,14 @@ export const useKeybindingStore = defineStore('keybinding', () => { } ) + const { wrapWithErrorHandling } = useErrorHandling() + processExtensionKeybindings((keybindings) => { + const addKeybinding = wrapWithErrorHandling(addDefaultKeybinding) + for (const keybinding of keybindings) { + addKeybinding(new KeybindingImpl(keybinding)) + } + }) + function getKeybindingsByCommandId(commandId: string) { return keybindingsByCommandId.value[commandId] ?? [] } diff --git a/src/utils/executableGroupNodeChildDTO.ts b/src/utils/executableGroupNodeChildDTO.ts index b129f2d103..d42daafa1b 100644 --- a/src/utils/executableGroupNodeChildDTO.ts +++ b/src/utils/executableGroupNodeChildDTO.ts @@ -1,4 +1,4 @@ -import type { GroupNodeHandler } from '@/extensions/core/groupNode' +import type { GroupNodeHandler } from '@/extensions/core/extensions/groupNode' import { ExecutableNodeDTO } from '@/lib/litegraph/src/litegraph' import type { ExecutableLGraphNode, diff --git a/tests-ui/tests/composables/useLoad3d.test.ts b/tests-ui/tests/composables/useLoad3d.test.ts index fadafa4f87..1ed282036f 100644 --- a/tests-ui/tests/composables/useLoad3d.test.ts +++ b/tests-ui/tests/composables/useLoad3d.test.ts @@ -2,16 +2,16 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { nextTick, ref } from 'vue' import { nodeToLoad3dMap, useLoad3d } from '@/composables/useLoad3d' -import Load3d from '@/extensions/core/load3d/Load3d' -import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' +import Load3d from '@/extensions/core/extensions/load3d/Load3d' +import Load3dUtils from '@/extensions/core/extensions/load3d/Load3dUtils' import { useToastStore } from '@/platform/updates/common/toastStore' import { api } from '@/scripts/api' -vi.mock('@/extensions/core/load3d/Load3d', () => ({ +vi.mock('@/extensions/core/extensions/load3d/Load3d', () => ({ default: vi.fn() })) -vi.mock('@/extensions/core/load3d/Load3dUtils', () => ({ +vi.mock('@/extensions/core/extensions/load3d/Load3dUtils', () => ({ default: { splitFilePath: vi.fn(), getResourceURL: vi.fn(), diff --git a/tests-ui/tests/composables/useLoad3dViewer.test.ts b/tests-ui/tests/composables/useLoad3dViewer.test.ts index 4ec2d77984..7b65d51e10 100644 --- a/tests-ui/tests/composables/useLoad3dViewer.test.ts +++ b/tests-ui/tests/composables/useLoad3dViewer.test.ts @@ -2,8 +2,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { nextTick } from 'vue' import { useLoad3dViewer } from '@/composables/useLoad3dViewer' -import Load3d from '@/extensions/core/load3d/Load3d' -import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' +import Load3d from '@/extensions/core/extensions/load3d/Load3d' +import Load3dUtils from '@/extensions/core/extensions/load3d/Load3dUtils' import { useToastStore } from '@/platform/updates/common/toastStore' import { useLoad3dService } from '@/services/load3dService' @@ -15,7 +15,7 @@ vi.mock('@/platform/updates/common/toastStore', () => ({ useToastStore: vi.fn() })) -vi.mock('@/extensions/core/load3d/Load3dUtils', () => ({ +vi.mock('@/extensions/core/extensions/load3d/Load3dUtils', () => ({ default: { uploadFile: vi.fn() } @@ -25,7 +25,7 @@ vi.mock('@/i18n', () => ({ t: vi.fn((key) => key) })) -vi.mock('@/extensions/core/load3d/Load3d', () => ({ +vi.mock('@/extensions/core/extensions/load3d/Load3d', () => ({ default: vi.fn() })) diff --git a/tests-ui/tests/maskeditor.test.ts b/tests-ui/tests/maskeditor.test.ts index 448c2cb422..4b60792007 100644 --- a/tests-ui/tests/maskeditor.test.ts +++ b/tests-ui/tests/maskeditor.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { imageLayerFilenamesIfApplicable } from '@/extensions/core/maskeditor/utils/maskEditorLayerFilenames' +import { imageLayerFilenamesIfApplicable } from '@/extensions/core/extensions/maskeditor/utils/maskEditorLayerFilenames' describe('imageLayerFilenamesIfApplicable', () => { // In case the naming scheme changes, this test will ensure CI fails if developers forget to support the old naming scheme. (Causing MaskEditor to lose layer data for previously-saved images.) diff --git a/tests-ui/tests/utils/executableGroupNodeChildDTO.test.ts b/tests-ui/tests/utils/executableGroupNodeChildDTO.test.ts index 6b2fb3ffa8..497fc211f3 100644 --- a/tests-ui/tests/utils/executableGroupNodeChildDTO.test.ts +++ b/tests-ui/tests/utils/executableGroupNodeChildDTO.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { GroupNodeHandler } from '@/extensions/core/groupNode' +import type { GroupNodeHandler } from '@/extensions/core/extensions/groupNode' import type { ExecutableLGraphNode, ExecutionId,