mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-08 06:30:04 +00:00
* refactor: move settingStore to platform/settings Move src/stores/settingStore.ts to src/platform/settings/settingStore.ts to separate platform infrastructure from domain logic following DDD principles. Updates all import references across ~70 files to maintain compatibility. * fix: update remaining settingStore imports after rebase * fix: complete remaining settingStore import updates * fix: update vi.mock paths for settingStore in tests Update all test files to mock the new settingStore location at @/platform/settings/settingStore instead of @/stores/settingStore * fix: resolve remaining settingStore imports and unused imports after rebase * fix: update settingStore mock path in SelectionToolbox test Fix vi.mock path from @/stores/settingStore to @/platform/settings/settingStore to resolve failing Load3D viewer button test. * refactor: complete comprehensive settings migration to platform layer This commit completes the migration of all settings-related code to the platform layer as part of the Domain-Driven Design (DDD) architecture refactoring. - constants/coreSettings.ts → platform/settings/constants/coreSettings.ts - types/settingTypes.ts → platform/settings/types.ts - stores/settingStore.ts → platform/settings/settingStore.ts (already moved) - composables/setting/useSettingUI.ts → platform/settings/composables/useSettingUI.ts - composables/setting/useSettingSearch.ts → platform/settings/composables/useSettingSearch.ts - composables/useLitegraphSettings.ts → platform/settings/composables/useLitegraphSettings.ts - components/dialog/content/SettingDialogContent.vue → platform/settings/components/SettingDialogContent.vue - components/dialog/content/setting/SettingItem.vue → platform/settings/components/SettingItem.vue - components/dialog/content/setting/SettingGroup.vue → platform/settings/components/SettingGroup.vue - components/dialog/content/setting/SettingsPanel.vue → platform/settings/components/SettingsPanel.vue - components/dialog/content/setting/ColorPaletteMessage.vue → platform/settings/components/ColorPaletteMessage.vue - components/dialog/content/setting/ExtensionPanel.vue → platform/settings/components/ExtensionPanel.vue - components/dialog/content/setting/ServerConfigPanel.vue → platform/settings/components/ServerConfigPanel.vue - ~100+ import statements updated across the codebase - Test file imports corrected - Component imports fixed in dialog service and command menubar - Composable imports updated in GraphCanvas.vue ``` src/platform/settings/ ├── components/ # All settings UI components ├── composables/ # Settings-related composables ├── constants/ # Core settings definitions ├── types.ts # Settings type definitions └── settingStore.ts # Central settings state management ``` ✅ TypeScript compilation successful ✅ All tests passing (settings store, search functionality, UI components) ✅ Production build successful ✅ Domain boundaries properly established This migration consolidates all settings functionality into a cohesive platform domain, improving maintainability and following DDD principles for better code organization. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: format and lint after rebase conflict resolution * fix: update remaining import paths to platform settings - Fix browser test import: extensionAPI.spec.ts - Fix script import: collect-i18n-general.ts - Complete settings migration import path updates 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
234 lines
5.7 KiB
Vue
234 lines
5.7 KiB
Vue
<template>
|
|
<div
|
|
ref="positionRef"
|
|
class="absolute left-1/2 -translate-x-1/2"
|
|
:class="positions.positioner"
|
|
></div>
|
|
<Popover
|
|
ref="popoverRef"
|
|
append-to="body"
|
|
:pt="{
|
|
root: {
|
|
class: 'workflow-popover-fade fit-content ' + positions.root,
|
|
'data-popover-id': id,
|
|
style: {
|
|
transform: positions.active
|
|
}
|
|
}
|
|
}"
|
|
@mouseenter="cancelHidePopover"
|
|
@mouseleave="hidePopover"
|
|
>
|
|
<div class="workflow-preview-content">
|
|
<div
|
|
v-if="thumbnailUrl && !isActiveTab"
|
|
class="workflow-preview-thumbnail relative"
|
|
>
|
|
<img
|
|
:src="thumbnailUrl"
|
|
class="block h-[200px] object-cover rounded-lg p-2"
|
|
:style="{ width: `${POPOVER_WIDTH}px` }"
|
|
/>
|
|
</div>
|
|
<div class="workflow-preview-footer">
|
|
<span class="workflow-preview-name">{{ workflowFilename }}</span>
|
|
</div>
|
|
</div>
|
|
</Popover>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import Popover from 'primevue/popover'
|
|
import { computed, nextTick, ref, toRefs, useId } from 'vue'
|
|
|
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
|
|
|
const POPOVER_WIDTH = 250
|
|
|
|
interface Props {
|
|
workflowFilename: string
|
|
thumbnailUrl?: string
|
|
isActiveTab: boolean
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
const { thumbnailUrl, isActiveTab } = toRefs(props)
|
|
|
|
const settingStore = useSettingStore()
|
|
const positions = computed<{
|
|
positioner: string
|
|
root?: string
|
|
active?: string
|
|
}>(() => {
|
|
if (
|
|
settingStore.get('Comfy.Workflow.WorkflowTabsPosition') === 'Topbar' &&
|
|
settingStore.get('Comfy.UseNewMenu') === 'Bottom'
|
|
) {
|
|
return {
|
|
positioner: 'top-0',
|
|
root: 'p-popover-flipped',
|
|
active: isActiveTab.value ? 'translateY(-100%)' : undefined
|
|
}
|
|
}
|
|
|
|
return {
|
|
positioner: 'bottom-0'
|
|
}
|
|
})
|
|
|
|
const popoverRef = ref<InstanceType<typeof Popover> | null>(null)
|
|
const positionRef = ref<HTMLElement | null>(null)
|
|
let hideTimeout: ReturnType<typeof setTimeout> | null = null
|
|
let showTimeout: ReturnType<typeof setTimeout> | null = null
|
|
const id = useId()
|
|
|
|
const showPopover = (event: Event) => {
|
|
// Clear any existing timeouts
|
|
if (hideTimeout) {
|
|
clearTimeout(hideTimeout)
|
|
hideTimeout = null
|
|
}
|
|
if (showTimeout) {
|
|
clearTimeout(showTimeout)
|
|
showTimeout = null
|
|
}
|
|
|
|
// Show popover after a short delay
|
|
showTimeout = setTimeout(async () => {
|
|
if (popoverRef.value && positionRef.value) {
|
|
popoverRef.value.show(event, positionRef.value)
|
|
await nextTick()
|
|
// PrimeVue has a bug where when the tabs are scrolled, it positions the element incorrectly
|
|
// Manually set the position to the middle of the tab and prevent it from going off the left/right edge
|
|
const el = document.querySelector(
|
|
`.workflow-popover-fade[data-popover-id="${id}"]`
|
|
) as HTMLElement
|
|
if (el) {
|
|
const middle = positionRef.value!.getBoundingClientRect().left
|
|
const popoverWidth = el.getBoundingClientRect().width
|
|
const halfWidth = popoverWidth / 2
|
|
let pos = middle - halfWidth
|
|
let shift = 0
|
|
|
|
// Calculate shift when clamping is needed
|
|
if (pos < 0) {
|
|
shift = pos - 8 // Negative shift to move arrow left
|
|
pos = 8
|
|
} else if (pos + popoverWidth > window.innerWidth) {
|
|
const newPos = window.innerWidth - popoverWidth - 16
|
|
shift = pos - newPos // Positive shift to move arrow right
|
|
pos = newPos
|
|
}
|
|
|
|
if (shift + halfWidth < 0) {
|
|
shift = -halfWidth + 24
|
|
}
|
|
|
|
el.style.left = `${pos}px`
|
|
el.style.setProperty('--shift', `${shift}px`)
|
|
}
|
|
}
|
|
}, 200) // 200ms delay before showing
|
|
}
|
|
|
|
const cancelHidePopover = () => {
|
|
// Temporarily disable this functionality until we need the popover to be interactive:
|
|
/*
|
|
if (hideTimeout) {
|
|
clearTimeout(hideTimeout)
|
|
hideTimeout = null
|
|
}
|
|
*/
|
|
}
|
|
|
|
const hidePopover = () => {
|
|
// Clear show timeout if mouse leaves before popover appears
|
|
if (showTimeout) {
|
|
clearTimeout(showTimeout)
|
|
showTimeout = null
|
|
}
|
|
|
|
hideTimeout = setTimeout(() => {
|
|
if (popoverRef.value) {
|
|
popoverRef.value.hide()
|
|
}
|
|
}, 100) // Minimal delay to allow moving to popover
|
|
}
|
|
|
|
const togglePopover = (event: Event) => {
|
|
if (popoverRef.value) {
|
|
popoverRef.value.toggle(event)
|
|
}
|
|
}
|
|
|
|
defineExpose({
|
|
showPopover,
|
|
hidePopover,
|
|
togglePopover
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
@reference '../../assets/css/style.css';
|
|
|
|
.workflow-preview-content {
|
|
@apply flex flex-col rounded-xl overflow-hidden;
|
|
max-width: var(--popover-width);
|
|
background-color: var(--comfy-menu-secondary-bg);
|
|
color: var(--fg-color);
|
|
}
|
|
|
|
.workflow-preview-thumbnail {
|
|
@apply relative p-2;
|
|
}
|
|
|
|
.workflow-preview-thumbnail img {
|
|
@apply shadow-md;
|
|
background-color: color-mix(
|
|
in srgb,
|
|
var(--comfy-menu-secondary-bg) 70%,
|
|
black
|
|
);
|
|
}
|
|
|
|
.dark-theme .workflow-preview-thumbnail img {
|
|
@apply shadow-lg;
|
|
}
|
|
|
|
.workflow-preview-footer {
|
|
@apply pt-1 pb-2 px-3;
|
|
}
|
|
|
|
.workflow-preview-name {
|
|
@apply block text-sm font-medium overflow-hidden text-ellipsis whitespace-nowrap;
|
|
color: var(--fg-color);
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
@reference '../../assets/css/style.css';
|
|
|
|
.workflow-popover-fade {
|
|
--p-popover-background: transparent;
|
|
--p-popover-content-padding: 0;
|
|
@apply bg-transparent rounded-xl shadow-lg;
|
|
transition: opacity 0.15s ease-out !important;
|
|
}
|
|
|
|
.workflow-popover-fade.p-popover-flipped {
|
|
@apply -translate-y-full;
|
|
}
|
|
|
|
.dark-theme .workflow-popover-fade {
|
|
@apply shadow-2xl;
|
|
}
|
|
|
|
.workflow-popover-fade.p-popover:after,
|
|
.workflow-popover-fade.p-popover:before {
|
|
--p-popover-border-color: var(--comfy-menu-secondary-bg);
|
|
left: 50%;
|
|
transform: translateX(calc(-50% + var(--shift)));
|
|
margin-left: 0;
|
|
}
|
|
</style>
|