()
const { t } = useI18n()
+const settingStore = useSettingStore()
+const settingValue = computed(() => settingStore.get(props.setting.id))
-function translateOptions(options: (SettingOption | string)[]) {
+function translateOptions(
+ options:
+ | (SettingOption | string)[]
+ | ((value?: unknown) => (SettingOption | string)[])
+): { text: string; value: string | number | undefined }[] {
if (typeof options === 'function') {
- // @ts-expect-error: Audit and deprecate usage of legacy options type:
- // (value) => [string | {text: string, value: string}]
- return translateOptions(options(props.setting.value ?? ''))
+ return translateOptions(options(settingValue.value))
}
return options.map((option) => {
@@ -75,9 +79,6 @@ const formItem = computed(() => {
: undefined
}
})
-
-const settingStore = useSettingStore()
-const settingValue = computed(() => settingStore.get(props.setting.id))
const updateSettingValue = async (
newValue: Settings[K]
) => {
diff --git a/src/platform/settings/composables/useInputDeviceDetection.ts b/src/platform/settings/composables/useInputDeviceDetection.ts
new file mode 100644
index 0000000000..ea5f7c514d
--- /dev/null
+++ b/src/platform/settings/composables/useInputDeviceDetection.ts
@@ -0,0 +1,11 @@
+import { ref } from 'vue'
+
+/**
+ * Reactive snapshot of the device the canvas pointer auto-detection currently
+ * believes is in use. Updated by a callback wired in useLitegraphSettings.
+ */
+const detectedInputDevice = ref<'mouse' | 'trackpad'>('mouse')
+
+export function useInputDeviceDetection() {
+ return { detectedInputDevice }
+}
diff --git a/src/platform/settings/composables/useLitegraphSettings.ts b/src/platform/settings/composables/useLitegraphSettings.ts
index 0a13ce29d7..3abb5b2c2d 100644
--- a/src/platform/settings/composables/useLitegraphSettings.ts
+++ b/src/platform/settings/composables/useLitegraphSettings.ts
@@ -5,6 +5,7 @@ import {
LGraphNode,
LiteGraph
} from '@/lib/litegraph/src/litegraph'
+import { useInputDeviceDetection } from '@/platform/settings/composables/useInputDeviceDetection'
import { useSettingStore } from '@/platform/settings/settingStore'
// eslint-disable-next-line import-x/no-restricted-paths
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
@@ -141,16 +142,6 @@ export const useLitegraphSettings = () => {
)
})
- watchEffect(() => {
- const navigationMode = settingStore.get('Comfy.Canvas.NavigationMode') as
- | 'standard'
- | 'legacy'
- | 'custom'
-
- LiteGraph.canvasNavigationMode = navigationMode
- LiteGraph.macTrackpadGestures = navigationMode === 'standard'
- })
-
watchEffect(() => {
const leftMouseBehavior = settingStore.get(
'Comfy.Canvas.LeftMouseClickBehavior'
@@ -159,10 +150,23 @@ export const useLitegraphSettings = () => {
})
watchEffect(() => {
- const mouseWheelScroll = settingStore.get(
- 'Comfy.Canvas.MouseWheelScroll'
- ) as 'panning' | 'zoom'
- LiteGraph.mouseWheelScroll = mouseWheelScroll
+ LiteGraph.wheelInputMode = settingStore.get(
+ 'Comfy.Graph.WheelInputMode'
+ ) as 'auto' | 'mouse' | 'trackpad'
+ })
+
+ /**
+ * Mirror the canvas pointer's auto-detected device onto a reactive ref so
+ * settings UI can show the current detection inside the "Auto" option.
+ */
+ watchEffect(() => {
+ const { canvas } = canvasStore
+ if (!canvas) return
+ const { detectedInputDevice } = useInputDeviceDetection()
+ detectedInputDevice.value = canvas.pointer.detectedDevice
+ canvas.pointer.onDetectedDeviceChange = (device) => {
+ detectedInputDevice.value = device
+ }
})
watchEffect(() => {
diff --git a/src/platform/settings/constants/coreSettings.ts b/src/platform/settings/constants/coreSettings.ts
index f805a4e067..fa38243495 100644
--- a/src/platform/settings/constants/coreSettings.ts
+++ b/src/platform/settings/constants/coreSettings.ts
@@ -1,6 +1,6 @@
import { LinkMarkerShape, LiteGraph } from '@/lib/litegraph/src/litegraph'
import { isCloud, isDesktop, isNightly } from '@/platform/distribution/types'
-import { useSettingStore } from '@/platform/settings/settingStore'
+import { useInputDeviceDetection } from '@/platform/settings/composables/useInputDeviceDetection'
import type { SettingParams } from '@/platform/settings/types'
import type { ColorPalettes } from '@/schemas/colorPaletteSchema'
import type { Keybinding } from '@/platform/keybindings/types'
@@ -159,101 +159,73 @@ export const CORE_SETTINGS: SettingParams[] = [
type: 'boolean',
defaultValue: false
},
+ {
+ id: 'Comfy.Graph.WheelInputMode',
+ category: ['LiteGraph', 'Canvas Navigation', 'InputDevice'],
+ name: 'Input device',
+ tooltip:
+ 'Forces the zoom formula for a specific input device. Use this if auto-detection misclassifies your device when switching between mouse and trackpad.',
+ type: 'combo',
+ defaultValue: 'auto',
+ sortOrder: 200,
+ /**
+ * Reactively label the auto option with the currently detected device.
+ * The function is called inside SettingItem's `formItem` computed, so any
+ * read of `detectedInputDevice.value` is tracked and refreshes the dropdown.
+ */
+ options: () => {
+ const { detectedInputDevice } = useInputDeviceDetection()
+ const autoLabel =
+ detectedInputDevice.value === 'trackpad'
+ ? 'Auto-detect (Trackpad)'
+ : 'Auto-detect (Mouse)'
+ return [
+ { value: 'auto', text: autoLabel },
+ { value: 'mouse', text: 'Mouse' },
+ { value: 'trackpad', text: 'Trackpad' }
+ ]
+ },
+ versionAdded: '1.45.0'
+ },
{
id: 'Comfy.Canvas.NavigationMode',
category: ['LiteGraph', 'Canvas Navigation', 'NavigationMode'],
name: 'Navigation Mode',
defaultValue: 'legacy',
- type: 'combo',
- sortOrder: 100,
+ type: 'hidden',
+ deprecated: true,
options: [
{ value: 'standard', text: 'Standard (New)' },
{ value: 'legacy', text: 'Drag Navigation' },
{ value: 'custom', text: 'Custom' }
],
- versionAdded: '1.25.0',
- defaultsByInstallVersion: {
- '1.25.0': 'legacy'
- },
- onChange: async (val: unknown, old?: unknown) => {
- const newValue = val as string
- const oldValue = old as string | undefined
- if (!oldValue) return
- const settingStore = useSettingStore()
-
- if (newValue === 'standard') {
- await settingStore.setMany({
- 'Comfy.Canvas.LeftMouseClickBehavior': 'select',
- 'Comfy.Canvas.MouseWheelScroll': 'panning'
- })
- } else if (newValue === 'legacy') {
- await settingStore.setMany({
- 'Comfy.Canvas.LeftMouseClickBehavior': 'panning',
- 'Comfy.Canvas.MouseWheelScroll': 'zoom'
- })
- }
- }
+ versionAdded: '1.25.0'
},
{
id: 'Comfy.Canvas.LeftMouseClickBehavior',
category: ['LiteGraph', 'Canvas Navigation', 'LeftMouseClickBehavior'],
name: 'Left Mouse Click Behavior',
- defaultValue: 'panning',
+ defaultValue: 'select',
type: 'radio',
- sortOrder: 50,
+ sortOrder: 100,
options: [
{ value: 'panning', text: 'Panning' },
{ value: 'select', text: 'Select' }
],
- versionAdded: '1.27.4',
- onChange: async (val: unknown) => {
- const newValue = val as string
- const settingStore = useSettingStore()
-
- const navigationMode = settingStore.get('Comfy.Canvas.NavigationMode')
-
- if (navigationMode !== 'custom') {
- if (
- (newValue === 'select' && navigationMode === 'standard') ||
- (newValue === 'panning' && navigationMode === 'legacy')
- ) {
- return
- }
-
- // only set to custom if it doesn't match the preset modes
- await settingStore.set('Comfy.Canvas.NavigationMode', 'custom')
- }
- }
+ versionAdded: '1.27.4'
},
{
id: 'Comfy.Canvas.MouseWheelScroll',
category: ['LiteGraph', 'Canvas Navigation', 'MouseWheelScroll'],
name: 'Mouse Wheel Scroll',
defaultValue: 'zoom',
- type: 'radio',
+ type: 'hidden',
+ deprecated: true,
options: [
{ value: 'panning', text: 'Panning' },
{ value: 'zoom', text: 'Zoom in/out' }
],
- versionAdded: '1.27.4',
- onChange: async (val: unknown) => {
- const newValue = val as string
- const settingStore = useSettingStore()
-
- const navigationMode = settingStore.get('Comfy.Canvas.NavigationMode')
-
- if (navigationMode !== 'custom') {
- if (
- (newValue === 'panning' && navigationMode === 'standard') ||
- (newValue === 'zoom' && navigationMode === 'legacy')
- ) {
- return
- }
-
- // only set to custom if it doesn't match the preset modes
- await settingStore.set('Comfy.Canvas.NavigationMode', 'custom')
- }
- }
+ versionAdded: '1.27.4'
},
{
id: 'Comfy.Graph.CanvasInfo',
diff --git a/src/platform/settings/types.ts b/src/platform/settings/types.ts
index 62139cb6fd..1f03611b28 100644
--- a/src/platform/settings/types.ts
+++ b/src/platform/settings/types.ts
@@ -57,7 +57,9 @@ export interface FormItem {
type: SettingInputType | SettingCustomRenderer
tooltip?: string
attrs?: Record
- options?: Array
+ options?:
+ | Array
+ | ((value?: unknown) => Array)
}
export interface ISettingGroup {
diff --git a/src/renderer/core/canvas/useCanvasInteractions.test.ts b/src/renderer/core/canvas/useCanvasInteractions.test.ts
index f73d717b70..f566289341 100644
--- a/src/renderer/core/canvas/useCanvasInteractions.test.ts
+++ b/src/renderer/core/canvas/useCanvasInteractions.test.ts
@@ -119,7 +119,7 @@ describe('useCanvasInteractions', () => {
describe('handleWheel', () => {
it('should forward ctrl+wheel events to canvas in standard nav mode', () => {
const { get } = useSettingStore()
- vi.mocked(get).mockReturnValue('standard')
+ vi.mocked(get).mockReturnValue('trackpad')
const { handleWheel } = useCanvasInteractions()
@@ -134,7 +134,7 @@ describe('useCanvasInteractions', () => {
it('should forward all wheel events to canvas in legacy nav mode', () => {
const { get } = useSettingStore()
- vi.mocked(get).mockReturnValue('legacy')
+ vi.mocked(get).mockReturnValue('mouse')
const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
@@ -146,7 +146,7 @@ describe('useCanvasInteractions', () => {
it('should not prevent default for regular wheel events in standard nav mode', () => {
const { get } = useSettingStore()
- vi.mocked(get).mockReturnValue('standard')
+ vi.mocked(get).mockReturnValue('trackpad')
const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
@@ -157,7 +157,7 @@ describe('useCanvasInteractions', () => {
})
it('should forward wheel events to canvas when capture element is NOT focused', () => {
const { get } = useSettingStore()
- vi.mocked(get).mockReturnValue('legacy')
+ vi.mocked(get).mockReturnValue('mouse')
const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
@@ -179,7 +179,7 @@ describe('useCanvasInteractions', () => {
it('should NOT forward wheel events when capture element IS focused', () => {
const { get } = useSettingStore()
- vi.mocked(get).mockReturnValue('legacy')
+ vi.mocked(get).mockReturnValue('mouse')
const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
@@ -202,7 +202,7 @@ describe('useCanvasInteractions', () => {
it('should forward ctrl+wheel to canvas when capture element IS focused in standard mode', () => {
const { get } = useSettingStore()
- vi.mocked(get).mockReturnValue('standard')
+ vi.mocked(get).mockReturnValue('trackpad')
const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
diff --git a/src/renderer/core/canvas/useCanvasInteractions.ts b/src/renderer/core/canvas/useCanvasInteractions.ts
index b93d4ff150..38907f2dd9 100644
--- a/src/renderer/core/canvas/useCanvasInteractions.ts
+++ b/src/renderer/core/canvas/useCanvasInteractions.ts
@@ -15,7 +15,7 @@ export function useCanvasInteractions() {
const { getCanvas } = canvasStore
const isStandardNavMode = computed(
- () => settingStore.get('Comfy.Canvas.NavigationMode') === 'standard'
+ () => settingStore.get('Comfy.Graph.WheelInputMode') === 'trackpad'
)
/**
diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts
index e7e40abdf7..5b9ec2ae46 100644
--- a/src/schemas/apiSchema.ts
+++ b/src/schemas/apiSchema.ts
@@ -319,6 +319,7 @@ const zSettings = z.object({
'Comfy.Graph.DeduplicateSubgraphNodeIds': z.boolean(),
'Comfy.Graph.LiveSelection': z.boolean(),
'Comfy.Graph.LinkMarkers': z.nativeEnum(LinkMarkerShape),
+ 'Comfy.Graph.WheelInputMode': z.enum(['auto', 'mouse', 'trackpad']),
'Comfy.Graph.ZoomSpeed': z.number(),
'Comfy.Group.DoubleClickTitleToEdit': z.boolean(),
'Comfy.GroupSelectedNodes.Padding': z.number(),