Fix: Minimap rendering (#7639)
## Summary Restores the refs, but in a way that vue-tsc understands. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7639-Fix-Minimap-rendering-2ce6d73d3650817eb323ce7e2022ab74) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
@@ -16,6 +16,7 @@
|
||||
/>
|
||||
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="litegraph-minimap relative border border-interface-stroke bg-comfy-menu-bg shadow-interface"
|
||||
:style="containerStyles"
|
||||
>
|
||||
@@ -50,7 +51,12 @@
|
||||
}"
|
||||
/>
|
||||
|
||||
<canvas :width="width" :height="height" class="minimap-canvas" />
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
:width="width"
|
||||
:height="height"
|
||||
class="minimap-canvas"
|
||||
/>
|
||||
|
||||
<div class="minimap-viewport" :style="viewportStyles" />
|
||||
|
||||
@@ -69,7 +75,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
|
||||
|
||||
import { useMinimap } from '@/renderer/extensions/minimap/composables/useMinimap'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
@@ -77,8 +83,9 @@ import { useCommandStore } from '@/stores/commandStore'
|
||||
import MiniMapPanel from './MiniMapPanel.vue'
|
||||
|
||||
const commandStore = useCommandStore()
|
||||
|
||||
const minimapRef = ref<HTMLDivElement>()
|
||||
const containerRef = useTemplateRef<HTMLDivElement>('containerRef')
|
||||
const canvasRef = useTemplateRef<HTMLCanvasElement>('canvasRef')
|
||||
|
||||
const {
|
||||
initialized,
|
||||
@@ -101,7 +108,10 @@ const {
|
||||
handlePointerCancel,
|
||||
handleWheel,
|
||||
setMinimapRef
|
||||
} = useMinimap()
|
||||
} = useMinimap({
|
||||
containerRefMaybe: containerRef,
|
||||
canvasRefMaybe: canvasRef
|
||||
})
|
||||
|
||||
const showOptionsPanel = ref(false)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useRafFn } from '@vueuse/core'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { computed, nextTick, ref, shallowRef, watch } from 'vue'
|
||||
import type { ShallowRef } from 'vue'
|
||||
|
||||
import type { LGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
@@ -13,14 +14,20 @@ import { useMinimapRenderer } from './useMinimapRenderer'
|
||||
import { useMinimapSettings } from './useMinimapSettings'
|
||||
import { useMinimapViewport } from './useMinimapViewport'
|
||||
|
||||
export function useMinimap() {
|
||||
export function useMinimap({
|
||||
canvasRefMaybe,
|
||||
containerRefMaybe
|
||||
}: {
|
||||
canvasRefMaybe?: Readonly<ShallowRef<HTMLCanvasElement | null>>
|
||||
containerRefMaybe?: Readonly<ShallowRef<HTMLDivElement | null>>
|
||||
} = {}) {
|
||||
const canvasStore = useCanvasStore()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const containerRef = ref<HTMLDivElement>()
|
||||
const canvasRef = ref<HTMLCanvasElement>()
|
||||
const minimapRef = ref<HTMLElement | null>(null)
|
||||
const canvasRef = canvasRefMaybe ?? shallowRef(null)
|
||||
const containerRef = containerRefMaybe ?? shallowRef(null)
|
||||
|
||||
const visible = ref(true)
|
||||
const initialized = ref(false)
|
||||
@@ -223,8 +230,6 @@ export function useMinimap() {
|
||||
visible: computed(() => visible.value),
|
||||
initialized: computed(() => initialized.value),
|
||||
|
||||
containerRef,
|
||||
canvasRef,
|
||||
containerStyles,
|
||||
viewportStyles,
|
||||
panelStyles,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { Ref, ShallowRef } from 'vue'
|
||||
|
||||
import type { MinimapCanvas } from '../types'
|
||||
|
||||
export function useMinimapInteraction(
|
||||
containerRef: Ref<HTMLDivElement | undefined>,
|
||||
containerRef: Readonly<ShallowRef<HTMLDivElement | null>>,
|
||||
bounds: Ref<{ minX: number; minY: number; width: number; height: number }>,
|
||||
scale: Ref<number>,
|
||||
width: number,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { Ref, ShallowRef } from 'vue'
|
||||
|
||||
import type { LGraph } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
@@ -7,7 +7,7 @@ import { renderMinimapToCanvas } from '../minimapCanvasRenderer'
|
||||
import type { UpdateFlags } from '../types'
|
||||
|
||||
export function useMinimapRenderer(
|
||||
canvasRef: Ref<HTMLCanvasElement | undefined>,
|
||||
canvasRef: Readonly<ShallowRef<HTMLCanvasElement | null>>,
|
||||
graph: Ref<LGraph | null>,
|
||||
bounds: Ref<{ minX: number; minY: number; width: number; height: number }>,
|
||||
scale: Ref<number>,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
import { nextTick, shallowRef } from 'vue'
|
||||
|
||||
const flushPromises = () => new Promise((resolve) => setTimeout(resolve, 0))
|
||||
|
||||
@@ -164,10 +164,11 @@ describe('useMinimap', () => {
|
||||
let mockContainerElement: any
|
||||
let mockContext2D: any
|
||||
|
||||
const createAndInitializeMinimap = async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
async function createAndInitializeMinimap() {
|
||||
const minimap = useMinimap({
|
||||
containerRefMaybe: shallowRef(mockContainerElement),
|
||||
canvasRefMaybe: shallowRef(mockCanvasElement)
|
||||
})
|
||||
await minimap.init()
|
||||
await nextTick()
|
||||
await flushPromises()
|
||||
@@ -301,10 +302,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should initialize minimap when canvas is available', async () => {
|
||||
const minimap = useMinimap()
|
||||
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -336,9 +334,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should setup event listeners on graph', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -349,9 +345,7 @@ describe('useMinimap', () => {
|
||||
|
||||
it('should handle visibility from settings', async () => {
|
||||
defaultSettingStore.get.mockReturnValue(false)
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -362,9 +356,7 @@ describe('useMinimap', () => {
|
||||
|
||||
describe('destroy', () => {
|
||||
it('should cleanup all resources', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
minimap.destroy()
|
||||
@@ -389,9 +381,7 @@ describe('useMinimap', () => {
|
||||
mockGraph.onNodeRemoved = originalCallbacks.onNodeRemoved
|
||||
mockGraph.onConnectionChange = originalCallbacks.onConnectionChange
|
||||
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
minimap.destroy()
|
||||
@@ -429,9 +419,7 @@ describe('useMinimap', () => {
|
||||
|
||||
describe('rendering', () => {
|
||||
it('should verify context is obtained during render', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
const getContextSpy = vi.spyOn(mockCanvasElement, 'getContext')
|
||||
|
||||
@@ -456,9 +444,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should render at least once after initialization', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -498,9 +484,7 @@ describe('useMinimap', () => {
|
||||
it('should not render when context is null', async () => {
|
||||
mockCanvasElement.getContext = vi.fn().mockReturnValue(null)
|
||||
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
@@ -514,9 +498,7 @@ describe('useMinimap', () => {
|
||||
const originalNodes = [...mockGraph._nodes]
|
||||
mockGraph._nodes = []
|
||||
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -695,9 +677,7 @@ describe('useMinimap', () => {
|
||||
|
||||
describe('wheel interactions', () => {
|
||||
it('should handle wheel zoom in', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -720,9 +700,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should handle wheel zoom out', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -745,9 +723,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should respect zoom limits', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -770,9 +746,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should update container rect if needed', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -795,9 +769,7 @@ describe('useMinimap', () => {
|
||||
|
||||
describe('viewport updates', () => {
|
||||
it('should update viewport transform correctly', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
await nextTick()
|
||||
@@ -814,9 +786,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should handle canvas dimension updates', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -839,9 +809,7 @@ describe('useMinimap', () => {
|
||||
|
||||
describe('graph change handling', () => {
|
||||
it('should handle node addition', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -861,9 +829,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should handle node removal', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -878,9 +844,7 @@ describe('useMinimap', () => {
|
||||
})
|
||||
|
||||
it('should handle connection changes', async () => {
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -907,9 +871,7 @@ describe('useMinimap', () => {
|
||||
describe('edge cases', () => {
|
||||
it('should handle missing node outputs', async () => {
|
||||
mockGraph._nodes[0].outputs = null
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await expect(minimap.init()).resolves.not.toThrow()
|
||||
expect(minimap.initialized.value).toBe(true)
|
||||
@@ -919,9 +881,7 @@ describe('useMinimap', () => {
|
||||
mockGraph.links.link1.target_id = 'invalid-node'
|
||||
mockGraph.getNodeById.mockReturnValue(null)
|
||||
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await expect(minimap.init()).resolves.not.toThrow()
|
||||
expect(minimap.initialized.value).toBe(true)
|
||||
@@ -930,9 +890,7 @@ describe('useMinimap', () => {
|
||||
it('should handle high DPI displays', async () => {
|
||||
window.devicePixelRatio = 2
|
||||
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
@@ -942,9 +900,7 @@ describe('useMinimap', () => {
|
||||
it('should handle nodes without color', async () => {
|
||||
mockGraph._nodes[0].color = undefined
|
||||
|
||||
const minimap = useMinimap()
|
||||
minimap.containerRef.value = mockContainerElement
|
||||
minimap.canvasRef.value = mockCanvasElement
|
||||
const minimap = await createAndInitializeMinimap()
|
||||
|
||||
await minimap.init()
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ describe('useMinimapInteraction', () => {
|
||||
})
|
||||
|
||||
it('should handle null container gracefully', () => {
|
||||
const containerRef = ref<HTMLDivElement | undefined>(undefined)
|
||||
const containerRef = ref<HTMLDivElement | null>(null)
|
||||
const boundsRef = ref({ minX: 0, minY: 0, width: 500, height: 400 })
|
||||
const scaleRef = ref(0.5)
|
||||
const canvasRef = ref(mockCanvas as any)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
|
||||
import type { LGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { useMinimapRenderer } from '@/renderer/extensions/minimap/composables/useMinimapRenderer'
|
||||
@@ -32,7 +32,7 @@ describe('useMinimapRenderer', () => {
|
||||
})
|
||||
|
||||
it('should initialize with full redraw needed', () => {
|
||||
const canvasRef = ref(mockCanvas)
|
||||
const canvasRef = shallowRef<HTMLCanvasElement | null>(mockCanvas)
|
||||
const graphRef = ref(mockGraph as any)
|
||||
const boundsRef = ref({ minX: 0, minY: 0, width: 100, height: 100 })
|
||||
const scaleRef = ref(1)
|
||||
@@ -230,7 +230,7 @@ describe('useMinimapRenderer', () => {
|
||||
})
|
||||
|
||||
it('should handle null canvas gracefully', () => {
|
||||
const canvasRef = ref<HTMLCanvasElement | undefined>(undefined)
|
||||
const canvasRef = shallowRef<HTMLCanvasElement | null>(null)
|
||||
const graphRef = ref(mockGraph as any)
|
||||
const boundsRef = ref({ minX: 0, minY: 0, width: 100, height: 100 })
|
||||
const scaleRef = ref(1)
|
||||
|
||||