diff --git a/browser_tests/tests/minimap.spec.ts b/browser_tests/tests/minimap.spec.ts index 9275107cb2..366a6634d5 100644 --- a/browser_tests/tests/minimap.spec.ts +++ b/browser_tests/tests/minimap.spec.ts @@ -24,8 +24,14 @@ test.describe('Minimap', () => { const minimapViewport = minimapContainer.locator('.minimap-viewport') await expect(minimapViewport).toBeVisible() - await expect(minimapContainer).toHaveCSS('position', 'absolute') - await expect(minimapContainer).toHaveCSS('z-index', '1000') + await expect(minimapContainer).toHaveCSS('position', 'relative') + + // position and z-index validation moved to the parent container of the minimap + const minimapMainContainer = comfyPage.page.locator( + '.minimap-main-container' + ) + await expect(minimapMainContainer).toHaveCSS('position', 'absolute') + await expect(minimapMainContainer).toHaveCSS('z-index', '1000') }) test('Validate minimap toggle button state', async ({ comfyPage }) => { diff --git a/src/components/graph/MiniMap.vue b/src/components/graph/MiniMap.vue index 2e0fb1a3c1..0e47595f02 100644 --- a/src/components/graph/MiniMap.vue +++ b/src/components/graph/MiniMap.vue @@ -1,39 +1,68 @@ diff --git a/src/composables/useMinimap.ts b/src/composables/useMinimap.ts index 6eada5b6c2..c329769c41 100644 --- a/src/composables/useMinimap.ts +++ b/src/composables/useMinimap.ts @@ -2,13 +2,14 @@ import { useRafFn, useThrottleFn } from '@vueuse/core' import { computed, nextTick, ref, watch } from 'vue' import { useCanvasTransformSync } from '@/composables/canvas/useCanvasTransformSync' -import type { LGraphNode } from '@/lib/litegraph/src/litegraph' +import { LGraphEventMode, LGraphNode } from '@/lib/litegraph/src/litegraph' import type { NodeId } from '@/schemas/comfyWorkflowSchema' import { api } from '@/scripts/api' import { app } from '@/scripts/app' import { useCanvasStore } from '@/stores/graphStore' import { useSettingStore } from '@/stores/settingStore' import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore' +import { adjustColor } from '@/utils/colorUtil' interface GraphCallbacks { onNodeAdded?: (node: LGraphNode) => void @@ -16,6 +17,13 @@ interface GraphCallbacks { onConnectionChange?: (node: LGraphNode) => void } +export type MinimapOptionKey = + | 'Comfy.Minimap.NodeColors' + | 'Comfy.Minimap.ShowLinks' + | 'Comfy.Minimap.ShowGroups' + | 'Comfy.Minimap.RenderBypassState' + | 'Comfy.Minimap.RenderErrorState' + export function useMinimap() { const settingStore = useSettingStore() const canvasStore = useCanvasStore() @@ -27,6 +35,27 @@ export function useMinimap() { const visible = ref(true) + const nodeColors = computed(() => + settingStore.get('Comfy.Minimap.NodeColors') + ) + const showLinks = computed(() => settingStore.get('Comfy.Minimap.ShowLinks')) + const showGroups = computed(() => + settingStore.get('Comfy.Minimap.ShowGroups') + ) + const renderBypass = computed(() => + settingStore.get('Comfy.Minimap.RenderBypassState') + ) + const renderError = computed(() => + settingStore.get('Comfy.Minimap.RenderErrorState') + ) + + const updateOption = async (key: MinimapOptionKey, value: boolean) => { + await settingStore.set(key, value) + + needsFullRedraw.value = true + updateMinimap() + } + const initialized = ref(false) const bounds = ref({ minX: 0, @@ -63,10 +92,19 @@ export function useMinimap() { const nodeColor = computed( () => (isLightTheme.value ? '#3DA8E099' : '#0B8CE999') // lighter blue for light theme ) + const nodeColorDefault = computed( + () => (isLightTheme.value ? '#D9D9D9' : '#353535') // this is the default node color when using nodeColors setting + ) const linkColor = computed( - () => (isLightTheme.value ? '#FFB347' : '#F99614') // lighter orange for light theme + () => (isLightTheme.value ? '#616161' : '#B3B3B3') // lighter orange for light theme ) const slotColor = computed(() => linkColor.value) + const groupColor = computed(() => + isLightTheme.value ? '#A2D3EC' : '#1F547A' + ) + const bypassColor = computed(() => + isLightTheme.value ? '#DBDBDB' : '#4B184B' + ) const containerRect = ref({ left: 0, @@ -116,6 +154,14 @@ export function useMinimap() { borderRadius: '8px' })) + const panelStyles = computed(() => ({ + width: `210px`, + height: `${height}px`, + backgroundColor: isLightTheme.value ? '#FAF9F5' : '#15161C', + border: `1px solid ${isLightTheme.value ? '#ccc' : '#333'}`, + borderRadius: '8px' + })) + const viewportStyles = computed(() => ({ transform: `translate(${viewportTransform.value.x}px, ${viewportTransform.value.y}px)`, width: `${viewportTransform.value.width}px`, @@ -189,6 +235,25 @@ export function useMinimap() { return Math.min(scaleX, scaleY) * 0.9 } + const renderGroups = ( + ctx: CanvasRenderingContext2D, + offsetX: number, + offsetY: number + ) => { + const g = graph.value + if (!g || !g._groups || g._groups.length === 0) return + + for (const group of g._groups) { + const x = (group.pos[0] - bounds.value.minX) * scale.value + offsetX + const y = (group.pos[1] - bounds.value.minY) * scale.value + offsetY + const w = group.size[0] * scale.value + const h = group.size[1] * scale.value + + ctx.fillStyle = groupColor.value + ctx.fillRect(x, y, w, h) + } + } + const renderNodes = ( ctx: CanvasRenderingContext2D, offsetX: number, @@ -203,9 +268,29 @@ export function useMinimap() { const w = node.size[0] * scale.value const h = node.size[1] * scale.value + let color = nodeColor.value + + if (renderBypass.value && node.mode === LGraphEventMode.BYPASS) { + color = bypassColor.value + } else if (nodeColors.value) { + color = nodeColorDefault.value + + if (node.bgcolor) { + color = isLightTheme.value + ? adjustColor(node.bgcolor, { lightness: 0.5 }) + : node.bgcolor + } + } + // Render solid node blocks - ctx.fillStyle = nodeColor.value + ctx.fillStyle = color ctx.fillRect(x, y, w, h) + + if (renderError.value && node.has_errors) { + ctx.strokeStyle = '#FF0000' + ctx.lineWidth = 0.3 + ctx.strokeRect(x, y, w, h) + } } } @@ -218,9 +303,9 @@ export function useMinimap() { if (!g) return ctx.strokeStyle = linkColor.value - ctx.lineWidth = 1.4 + ctx.lineWidth = 0.3 - const slotRadius = 3.7 * Math.max(scale.value, 0.5) // Larger slots that scale + const slotRadius = Math.max(scale.value, 0.5) // Larger slots that scale const connections: Array<{ x1: number y1: number @@ -304,8 +389,15 @@ export function useMinimap() { const offsetX = (width - bounds.value.width * scale.value) / 2 const offsetY = (height - bounds.value.height * scale.value) / 2 + if (showGroups.value) { + renderGroups(ctx, offsetX, offsetY) + } + + if (showLinks.value) { + renderConnections(ctx, offsetX, offsetY) + } + renderNodes(ctx, offsetX, offsetY) - renderConnections(ctx, offsetX, offsetY) needsFullRedraw.value = false updateFlags.value.nodes = false @@ -690,9 +782,16 @@ export function useMinimap() { canvasRef, containerStyles, viewportStyles, + panelStyles, width, height, + nodeColors, + showLinks, + showGroups, + renderBypass, + renderError, + init, destroy, toggle, @@ -701,6 +800,7 @@ export function useMinimap() { handlePointerMove, handlePointerUp, handleWheel, - setMinimapRef + setMinimapRef, + updateOption } } diff --git a/src/constants/coreSettings.ts b/src/constants/coreSettings.ts index 0765bf61a7..621d6b38c6 100644 --- a/src/constants/coreSettings.ts +++ b/src/constants/coreSettings.ts @@ -830,6 +830,41 @@ export const CORE_SETTINGS: SettingParams[] = [ defaultValue: true, versionAdded: '1.25.0' }, + { + id: 'Comfy.Minimap.NodeColors', + name: 'Display node with its original color on minimap', + type: 'hidden', + defaultValue: false, + versionAdded: '1.26.0' + }, + { + id: 'Comfy.Minimap.ShowLinks', + name: 'Display links on minimap', + type: 'hidden', + defaultValue: true, + versionAdded: '1.26.0' + }, + { + id: 'Comfy.Minimap.ShowGroups', + name: 'Display node groups on minimap', + type: 'hidden', + defaultValue: true, + versionAdded: '1.26.0' + }, + { + id: 'Comfy.Minimap.RenderBypassState', + name: 'Render bypass state on minimap', + type: 'hidden', + defaultValue: true, + versionAdded: '1.26.0' + }, + { + id: 'Comfy.Minimap.RenderErrorState', + name: 'Render error state on minimap', + type: 'hidden', + defaultValue: true, + versionAdded: '1.26.0' + }, { id: 'Comfy.Workflow.AutoSaveDelay', name: 'Auto Save Delay (ms)', diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 891a50402a..cfd240d21f 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -1647,5 +1647,12 @@ "view": "View", "panelControls": "Panel Controls" } + }, + "minimap": { + "nodeColors": "Node Colors", + "showLinks": "Show Links", + "showGroups": "Show Frames/Groups", + "renderBypassState": "Render Bypass State", + "renderErrorState": "Render Error State" } } \ No newline at end of file diff --git a/src/locales/es/main.json b/src/locales/es/main.json index c168c0e36e..4803c6c757 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -831,6 +831,13 @@ "Zoom In": "Acercar", "Zoom Out": "Alejar" }, + "minimap": { + "nodeColors": "Colores de nodos", + "renderBypassState": "Mostrar estado de omisión", + "renderErrorState": "Mostrar estado de error", + "showGroups": "Mostrar marcos/grupos", + "showLinks": "Mostrar enlaces" + }, "missingModelsDialog": { "doNotAskAgain": "No mostrar esto de nuevo", "missingModels": "Modelos faltantes", diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index 5bf0434137..f04336fcc9 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -831,6 +831,13 @@ "Zoom In": "Zoom avant", "Zoom Out": "Zoom arrière" }, + "minimap": { + "nodeColors": "Couleurs des nœuds", + "renderBypassState": "Afficher l’état de contournement", + "renderErrorState": "Afficher l’état d’erreur", + "showGroups": "Afficher les cadres/groupes", + "showLinks": "Afficher les liens" + }, "missingModelsDialog": { "doNotAskAgain": "Ne plus afficher ce message", "missingModels": "Modèles manquants", diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 739c86491a..73a4f904c5 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -831,6 +831,13 @@ "Zoom In": "ズームイン", "Zoom Out": "ズームアウト" }, + "minimap": { + "nodeColors": "ノードの色", + "renderBypassState": "バイパス状態を表示", + "renderErrorState": "エラー状態を表示", + "showGroups": "フレーム/グループを表示", + "showLinks": "リンクを表示" + }, "missingModelsDialog": { "doNotAskAgain": "再度表示しない", "missingModels": "モデルが見つかりません", diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index 25ec517383..ec0999a0b2 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -831,6 +831,13 @@ "Zoom In": "확대", "Zoom Out": "축소" }, + "minimap": { + "nodeColors": "노드 색상", + "renderBypassState": "바이패스 상태 렌더링", + "renderErrorState": "에러 상태 렌더링", + "showGroups": "프레임/그룹 표시", + "showLinks": "링크 표시" + }, "missingModelsDialog": { "doNotAskAgain": "다시 보지 않기", "missingModels": "모델이 없습니다", diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index dd222dc2f2..ae4599a86b 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -831,6 +831,13 @@ "Zoom In": "Увеличить", "Zoom Out": "Уменьшить" }, + "minimap": { + "nodeColors": "Цвета узлов", + "renderBypassState": "Отображать состояние обхода", + "renderErrorState": "Отображать состояние ошибки", + "showGroups": "Показать фреймы/группы", + "showLinks": "Показать связи" + }, "missingModelsDialog": { "doNotAskAgain": "Больше не показывать это", "missingModels": "Отсутствующие модели", diff --git a/src/locales/zh-TW/main.json b/src/locales/zh-TW/main.json index 47edf5bb8a..d27f38f935 100644 --- a/src/locales/zh-TW/main.json +++ b/src/locales/zh-TW/main.json @@ -831,6 +831,13 @@ "Zoom In": "放大", "Zoom Out": "縮小" }, + "minimap": { + "nodeColors": "節點顏色", + "renderBypassState": "顯示繞過狀態", + "renderErrorState": "顯示錯誤狀態", + "showGroups": "顯示框架/群組", + "showLinks": "顯示連結" + }, "missingModelsDialog": { "doNotAskAgain": "不要再顯示此訊息", "missingModels": "缺少模型", diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index 189eb1f913..72c8e4d743 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -831,6 +831,13 @@ "Zoom In": "放大画面", "Zoom Out": "缩小画面" }, + "minimap": { + "nodeColors": "節點顏色", + "renderBypassState": "顯示繞過狀態", + "renderErrorState": "顯示錯誤狀態", + "showGroups": "顯示框架/群組", + "showLinks": "顯示連結" + }, "missingModelsDialog": { "doNotAskAgain": "不再显示此消息", "missingModels": "缺少模型", diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index 887d165379..7c90292656 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -471,6 +471,11 @@ const zSettings = z.object({ 'Comfy.InstalledVersion': z.string().nullable(), 'Comfy.Node.AllowImageSizeDraw': z.boolean(), 'Comfy.Minimap.Visible': z.boolean(), + 'Comfy.Minimap.NodeColors': z.boolean(), + 'Comfy.Minimap.ShowLinks': z.boolean(), + 'Comfy.Minimap.ShowGroups': z.boolean(), + 'Comfy.Minimap.RenderBypassState': z.boolean(), + 'Comfy.Minimap.RenderErrorState': z.boolean(), 'Comfy.Canvas.NavigationMode': z.string(), 'Comfy-Desktop.AutoUpdate': z.boolean(), 'Comfy-Desktop.SendStatistics': z.boolean(),