Files
ComfyUI_frontend/src/components/graph/GraphCanvasMenu.vue
Alexander Brown 3956fa68a8 fix: code quality improvements across codebase
- Fix STL export comma operator bug (originalURL was silently discarded)
- Remove redundant null checks, identical branches in CameraManager
- Simplify ViewHelperManager.visibleViewHelper boolean assignment
- Remove redundant inner length check in AnimationManager
- Extract calculateLetterbox helper to deduplicate aspect-ratio calcs
- Add explicit return types to ComfyApi public methods
- Extract registerAuthHook helper in extensionService
- Extract prepareConfigureData in litegraphService
- Rename NeverNever to OmitNeverProps for clarity
- Rename progressBarBackground.ts to useProgressBarBackground.ts
- Move tooltipConfig.ts to utils/ (not a composable)
- Remove restating comments and formulaic docstrings
- Add missing return type to getStorageValue
- Remove unnecessary runtime validation in deprecated gridUtil
- Simplify updateControlWidgetLabel and ControlsManager zoom narrowing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:57:05 -08:00

238 lines
7.0 KiB
Vue

<template>
<div>
<ZoomControlsModal :visible="isPopoverOpen" @close="hidePopover" />
<!-- Backdrop -->
<div
v-if="hasActivePopup"
class="fixed inset-0 z-1200"
@click="hidePopover"
></div>
<ButtonGroup
class="absolute right-0 bottom-0 z-1200 flex-row gap-1 border border-interface-stroke bg-comfy-menu-bg p-2"
:style="{
...stringifiedMinimapStyles.buttonGroupStyles
}"
@wheel="canvasInteractions.handleWheel"
>
<CanvasModeSelector
:button-styles="stringifiedMinimapStyles.buttonStyles"
/>
<div class="h-[27px] w-px self-center bg-node-divider" />
<Button
v-tooltip.top="fitViewTooltip"
variant="secondary"
:aria-label="fitViewTooltip"
:style="stringifiedMinimapStyles.buttonStyles"
class="size-8 bg-comfy-menu-bg p-0 hover:bg-interface-button-hover-surface!"
@click="() => commandStore.execute('Comfy.Canvas.FitView')"
>
<i class="icon-[lucide--focus] size-4" />
</Button>
<Button
v-tooltip.top="t('zoomControls.label')"
variant="secondary"
:class="zoomButtonClass"
:aria-label="t('zoomControls.label')"
data-testid="zoom-controls-button"
:style="stringifiedMinimapStyles.buttonStyles"
@click="togglePopover"
>
<span class="inline-flex items-center gap-1 px-2 text-xs">
<span>{{ canvasStore.appScalePercentage }}%</span>
<i class="icon-[lucide--chevron-down] size-4" />
</span>
</Button>
<div class="h-[27px] w-px self-center bg-node-divider" />
<Button
v-tooltip.top="minimapTooltip"
variant="secondary"
:aria-label="minimapTooltip"
data-testid="toggle-minimap-button"
:style="stringifiedMinimapStyles.buttonStyles"
:class="minimapButtonClass"
@click="onMinimapToggleClick"
>
<i class="icon-[lucide--map] size-4" />
</Button>
<Button
v-tooltip.top="{
value: linkVisibilityTooltip,
pt: {
root: {
style: 'z-index: 2; transform: translateY(-20px);'
}
}
}"
variant="secondary"
:class="linkVisibleClass"
:aria-label="linkVisibilityAriaLabel"
data-testid="toggle-link-visibility-button"
:style="stringifiedMinimapStyles.buttonStyles"
@click="onLinkVisibilityToggleClick"
>
<i class="icon-[lucide--route-off] size-4" />
</Button>
</ButtonGroup>
</div>
</template>
<script setup lang="ts">
import ButtonGroup from 'primevue/buttongroup'
import { computed, onBeforeUnmount, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import Button from '@/components/ui/button/Button.vue'
import { useZoomControls } from '@/composables/useZoomControls'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
import { useMinimap } from '@/renderer/extensions/minimap/composables/useMinimap'
import { useCommandStore } from '@/stores/commandStore'
import CanvasModeSelector from './CanvasModeSelector.vue'
import ZoomControlsModal from './modals/ZoomControlsModal.vue'
const { t } = useI18n()
const commandStore = useCommandStore()
const { formatKeySequence } = useCommandStore()
const canvasStore = useCanvasStore()
const settingStore = useSettingStore()
const canvasInteractions = useCanvasInteractions()
const minimap = useMinimap()
const { isPopoverOpen, togglePopover, hidePopover, hasActivePopup } =
useZoomControls()
const stringifiedMinimapStyles = computed(() => {
const buttonGroupKeys = ['borderRadius']
const buttonKeys = ['borderRadius']
const additionalButtonStyles = {
border: 'none'
}
const containerStyles = minimap.containerStyles.value
const buttonStyles = {
...Object.fromEntries(
Object.entries(containerStyles).filter(([key]) =>
buttonKeys.includes(key)
)
),
...additionalButtonStyles
}
const buttonGroupStyles = Object.entries(containerStyles)
.filter(([key]) => buttonGroupKeys.includes(key))
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
return { buttonStyles, buttonGroupStyles }
})
// Computed properties for reactive states
const linkHidden = computed(
() => settingStore.get('Comfy.LinkRenderMode') === LiteGraph.HIDDEN_LINK
)
// Computed properties for command text
const fitViewCommandText = computed(() =>
formatKeySequence(
commandStore.getCommand('Comfy.Canvas.FitView')
).toUpperCase()
)
const minimapCommandText = computed(() =>
formatKeySequence(
commandStore.getCommand('Comfy.Canvas.ToggleMinimap')
).toUpperCase()
)
// Computed properties for button classes and states
const zoomButtonClass = computed(() => [
'bg-comfy-menu-bg',
isPopoverOpen.value ? 'not-active:bg-interface-panel-selected-surface!' : '',
'hover:bg-interface-button-hover-surface!',
'p-0',
'h-8',
'w-15'
])
const minimapButtonClass = computed(() => ({
'bg-comfy-menu-bg': true,
'hover:bg-interface-button-hover-surface!': true,
'not-active:bg-interface-panel-selected-surface!': settingStore.get(
'Comfy.Minimap.Visible'
),
'p-0': true,
'w-8': true,
'h-8': true
}))
// Computed properties for tooltip and aria-label texts
const fitViewTooltip = computed(() => {
const label = t('graphCanvasMenu.fitView')
const shortcut = fitViewCommandText.value
return shortcut ? `${label} (${shortcut})` : label
})
const minimapTooltip = computed(() => {
const label = settingStore.get('Comfy.Minimap.Visible')
? t('zoomControls.hideMinimap')
: t('zoomControls.showMinimap')
const shortcut = minimapCommandText.value
return shortcut ? `${label} (${shortcut})` : label
})
const linkVisibilityTooltip = computed(() =>
linkHidden.value
? t('graphCanvasMenu.showLinks')
: t('graphCanvasMenu.hideLinks')
)
const linkVisibilityAriaLabel = computed(() =>
linkHidden.value
? t('graphCanvasMenu.showLinks')
: t('graphCanvasMenu.hideLinks')
)
const linkVisibleClass = computed(() => [
'bg-comfy-menu-bg',
linkHidden.value ? 'not-active:bg-interface-panel-selected-surface!' : '',
'hover:bg-interface-button-hover-surface!',
'p-0',
'w-8',
'h-8'
])
onMounted(() => {
canvasStore.initScaleSync()
})
/**
* Track minimap toggle button click and execute the command.
*/
const onMinimapToggleClick = () => {
useTelemetry()?.trackUiButtonClicked({
button_id: 'graph_menu_minimap_toggle_clicked'
})
void commandStore.execute('Comfy.Canvas.ToggleMinimap')
}
/**
* Track hide/show links button click and execute the command.
*/
const onLinkVisibilityToggleClick = () => {
useTelemetry()?.trackUiButtonClicked({
button_id: 'graph_menu_hide_links_toggle_clicked'
})
void commandStore.execute('Comfy.Canvas.ToggleLinkVisibility')
}
onBeforeUnmount(() => {
canvasStore.cleanupScaleSync()
})
</script>