From c5c54df753ac25d41c180c4359c20d6ba2dd50d0 Mon Sep 17 00:00:00 2001 From: snomiao Date: Thu, 18 Sep 2025 12:47:05 +0000 Subject: [PATCH] fix: remove unused dependencies and files flagged by knip --- package.json | 13 - src/renderer/core/layout/TransformPane.vue | 91 ------- .../layout/slots/useDomSlotRegistration.ts | 229 ---------------- src/renderer/core/layout/useTransformState.ts | 246 ------------------ 4 files changed, 579 deletions(-) delete mode 100644 src/renderer/core/layout/TransformPane.vue delete mode 100644 src/renderer/core/layout/slots/useDomSlotRegistration.ts delete mode 100644 src/renderer/core/layout/useTransformState.ts diff --git a/package.json b/package.json index 3c7963365..9737ff3dd 100644 --- a/package.json +++ b/package.json @@ -40,11 +40,6 @@ "build-storybook": "storybook build" }, "devDependencies": { - "@babel/core": "^7.28.4", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.28.0", - "@babel/preset-env": "^7.28.3", - "@babel/preset-typescript": "^7.27.1", "@eslint/js": "^9.8.0", "@iconify-json/lucide": "^1.2.66", "@iconify/tailwind": "^1.2.0", @@ -69,15 +64,7 @@ "@vitejs/plugin-vue": "^5.1.4", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.0.0", - "@vue/babel-plugin-jsx": "^1.5.0", "@vue/test-utils": "^2.4.6", - "babel-helper-vue-jsx-merge-props": "^2.0.3", - "babel-plugin-module-resolver": "^5.0.2", - "babel-plugin-syntax-jsx": "^6.18.0", - "babel-plugin-transform-import-ignore": "^1.1.0", - "babel-plugin-transform-vue-jsx": "^3.7.0", - "babel-preset-env": "^1.7.0", - "babel-preset-typescript-vue3": "^2.1.1", "eslint": "^9.34.0", "eslint-config-prettier": "^10.1.2", "eslint-plugin-prettier": "^5.2.6", diff --git a/src/renderer/core/layout/TransformPane.vue b/src/renderer/core/layout/TransformPane.vue deleted file mode 100644 index 2f623257c..000000000 --- a/src/renderer/core/layout/TransformPane.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - - - diff --git a/src/renderer/core/layout/slots/useDomSlotRegistration.ts b/src/renderer/core/layout/slots/useDomSlotRegistration.ts deleted file mode 100644 index 94a1f09e5..000000000 --- a/src/renderer/core/layout/slots/useDomSlotRegistration.ts +++ /dev/null @@ -1,229 +0,0 @@ -/** - * DOM-based slot registration with performance optimization - * - * Measures the actual DOM position of a Vue slot connector and registers it - * into the LayoutStore so hit-testing and link rendering use the true position. - * - * Performance strategy: - * - Cache slot offset relative to node (avoids DOM reads during drag) - * - No measurements during pan/zoom (camera transforms don't change canvas coords) - * - Batch DOM reads via requestAnimationFrame - * - Only remeasure on structural changes (resize, collapse, LOD) - */ -import { - type Ref, - type WatchStopHandle, - nextTick, - onMounted, - onUnmounted, - ref, - watch -} from 'vue' - -import { LiteGraph } from '@/lib/litegraph/src/litegraph' -import { layoutStore } from '@/renderer/core/layout/store/layoutStore' -import type { Point as LayoutPoint } from '@/renderer/core/layout/types' - -import { getSlotKey } from './slotIdentifier' - -export type TransformState = { - screenToCanvas: (p: LayoutPoint) => LayoutPoint -} - -// Shared RAF queue for batching measurements -const measureQueue = new Set<() => void>() -let rafId: number | null = null -// Track mounted components to prevent execution on unmounted ones -const mountedComponents = new WeakSet() - -function scheduleMeasurement(fn: () => void) { - measureQueue.add(fn) - if (rafId === null) { - rafId = requestAnimationFrame(() => { - rafId = null - const batch = Array.from(measureQueue) - measureQueue.clear() - batch.forEach((measure) => measure()) - }) - } -} - -const cleanupFunctions = new WeakMap< - Ref, - { - stopWatcher?: WatchStopHandle - handleResize?: () => void - } ->() - -interface SlotRegistrationOptions { - nodeId: string - slotIndex: number - isInput: boolean - element: Ref - transform?: TransformState -} - -export function useDomSlotRegistration(options: SlotRegistrationOptions) { - const { nodeId, slotIndex, isInput, element: elRef, transform } = options - - // Early return if no nodeId - if (!nodeId || nodeId === '') { - return { - remeasure: () => {} - } - } - const slotKey = getSlotKey(nodeId, slotIndex, isInput) - // Track if this component is mounted - const componentToken = {} - - // Cached offset from node position (avoids DOM reads during drag) - const cachedOffset = ref(null) - const lastMeasuredBounds = ref(null) - - // Measure DOM and cache offset (expensive, minimize calls) - const measureAndCacheOffset = () => { - // Skip if component was unmounted - if (!mountedComponents.has(componentToken)) return - - const el = elRef.value - if (!el || !transform?.screenToCanvas) return - - const rect = el.getBoundingClientRect() - - // Skip if bounds haven't changed significantly (within 0.5px) - if (lastMeasuredBounds.value) { - const prev = lastMeasuredBounds.value - if ( - Math.abs(rect.left - prev.left) < 0.5 && - Math.abs(rect.top - prev.top) < 0.5 && - Math.abs(rect.width - prev.width) < 0.5 && - Math.abs(rect.height - prev.height) < 0.5 - ) { - return // No significant change - skip update - } - } - - lastMeasuredBounds.value = rect - - // Center of the visual connector (dot) in screen coords - const centerScreen = { - x: rect.left + rect.width / 2, - y: rect.top + rect.height / 2 - } - const centerCanvas = transform.screenToCanvas(centerScreen) - - // Cache offset from node position for fast updates during drag - const nodeLayout = layoutStore.getNodeLayoutRef(nodeId).value - if (nodeLayout) { - cachedOffset.value = { - x: centerCanvas.x - nodeLayout.position.x, - y: centerCanvas.y - nodeLayout.position.y - } - } - - updateSlotPosition(centerCanvas) - } - - // Fast update using cached offset (no DOM read) - const updateFromCachedOffset = () => { - if (!cachedOffset.value) { - // No cached offset yet, need to measure - scheduleMeasurement(measureAndCacheOffset) - return - } - - const nodeLayout = layoutStore.getNodeLayoutRef(nodeId).value - if (!nodeLayout) { - return - } - - // Calculate absolute position from node position + cached offset - const centerCanvas = { - x: nodeLayout.position.x + cachedOffset.value.x, - y: nodeLayout.position.y + cachedOffset.value.y - } - - updateSlotPosition(centerCanvas) - } - - // Update slot position in layout store - const updateSlotPosition = (centerCanvas: LayoutPoint) => { - const size = LiteGraph.NODE_SLOT_HEIGHT - const half = size / 2 - - layoutStore.updateSlotLayout(slotKey, { - nodeId, - index: slotIndex, - type: isInput ? 'input' : 'output', - position: { x: centerCanvas.x, y: centerCanvas.y }, - bounds: { - x: centerCanvas.x - half, - y: centerCanvas.y - half, - width: size, - height: size - } - }) - } - - onMounted(async () => { - // Mark component as mounted - mountedComponents.add(componentToken) - - // Initial measure after mount - await nextTick() - measureAndCacheOffset() - - // Subscribe to node position changes for fast cached updates - const nodeRef = layoutStore.getNodeLayoutRef(nodeId) - - const stopWatcher = watch( - nodeRef, - (newLayout) => { - if (newLayout) { - // Node moved/resized - update using cached offset - updateFromCachedOffset() - } - }, - { immediate: false } - ) - - // Store cleanup functions without type assertions - const cleanup = cleanupFunctions.get(elRef) || {} - cleanup.stopWatcher = stopWatcher - - // Window resize - remeasure as viewport changed - const handleResize = () => { - scheduleMeasurement(measureAndCacheOffset) - } - window.addEventListener('resize', handleResize, { passive: true }) - cleanup.handleResize = handleResize - cleanupFunctions.set(elRef, cleanup) - }) - - onUnmounted(() => { - // Mark component as unmounted - mountedComponents.delete(componentToken) - - // Clean up watchers and listeners - const cleanup = cleanupFunctions.get(elRef) - if (cleanup) { - if (cleanup.stopWatcher) cleanup.stopWatcher() - if (cleanup.handleResize) { - window.removeEventListener('resize', cleanup.handleResize) - } - cleanupFunctions.delete(elRef) - } - - // Remove from layout store - layoutStore.deleteSlotLayout(slotKey) - - // Remove from measurement queue if pending - measureQueue.delete(measureAndCacheOffset) - }) - - return { - // Expose for forced remeasure on structural changes - remeasure: () => scheduleMeasurement(measureAndCacheOffset) - } -} diff --git a/src/renderer/core/layout/useTransformState.ts b/src/renderer/core/layout/useTransformState.ts deleted file mode 100644 index d0ff76d4e..000000000 --- a/src/renderer/core/layout/useTransformState.ts +++ /dev/null @@ -1,246 +0,0 @@ -/** - * Composable for managing transform state synchronized with LiteGraph canvas - * - * This composable is a critical part of the hybrid rendering architecture that - * allows Vue components to render in perfect alignment with LiteGraph's canvas. - * - * ## Core Concept - * - * LiteGraph uses a canvas for rendering connections, grid, and handling interactions. - * Vue components need to render nodes on top of this canvas. The challenge is - * synchronizing the coordinate systems: - * - * - LiteGraph: Uses canvas coordinates with its own transform matrix - * - Vue/DOM: Uses screen coordinates with CSS transforms - * - * ## Solution: Transform Container Pattern - * - * Instead of transforming individual nodes (O(n) complexity), we: - * 1. Mirror LiteGraph's transform matrix to a single CSS container - * 2. Place all Vue nodes as children with simple absolute positioning - * 3. Achieve O(1) transform updates regardless of node count - * - * ## Coordinate Systems - * - * - **Canvas coordinates**: LiteGraph's internal coordinate system - * - **Screen coordinates**: Browser's viewport coordinate system - * - **Transform sync**: camera.x/y/z mirrors canvas.ds.offset/scale - * - * ## Performance Benefits - * - * - GPU acceleration via CSS transforms - * - No layout thrashing (only transform changes) - * - Efficient viewport culling calculations - * - Scales to 1000+ nodes while maintaining 60 FPS - * - * @example - * ```typescript - * const { camera, transformStyle, canvasToScreen } = useTransformState() - * - * // In template - *
- * - *
- * - * // Convert coordinates - * const screenPos = canvasToScreen({ x: nodeX, y: nodeY }) - * ``` - */ -import { computed, reactive, readonly } from 'vue' - -import type { LGraphCanvas } from '@/lib/litegraph/src/litegraph' - -interface Point { - x: number - y: number -} - -interface Camera { - x: number - y: number - z: number // scale/zoom -} - -export const useTransformState = () => { - // Reactive state mirroring LiteGraph's canvas transform - const camera = reactive({ - x: 0, - y: 0, - z: 1 - }) - - // Computed transform string for CSS - const transformStyle = computed(() => ({ - // Match LiteGraph DragAndScale.toCanvasContext(): - // ctx.scale(scale); ctx.translate(offset) - // CSS applies right-to-left, so "scale() translate()" -> translate first, then scale - // Effective mapping: screen = (canvas + offset) * scale - transform: `scale(${camera.z}) translate(${camera.x}px, ${camera.y}px)`, - transformOrigin: '0 0' - })) - - /** - * Synchronizes Vue's reactive camera state with LiteGraph's canvas transform - * - * Called every frame via RAF to ensure Vue components stay aligned with canvas. - * This is the heart of the hybrid rendering system - it bridges the gap between - * LiteGraph's canvas transforms and Vue's reactive system. - * - * @param canvas - LiteGraph canvas instance with DragAndScale (ds) transform state - */ - const syncWithCanvas = (canvas: LGraphCanvas) => { - if (!canvas || !canvas.ds) return - - // Mirror LiteGraph's transform state to Vue's reactive state - // ds.offset = pan offset, ds.scale = zoom level - camera.x = canvas.ds.offset[0] - camera.y = canvas.ds.offset[1] - camera.z = canvas.ds.scale || 1 - } - - /** - * Converts canvas coordinates to screen coordinates - * - * Applies the same transform that LiteGraph uses for rendering. - * Essential for positioning Vue components to align with canvas elements. - * - * Formula: screen = (canvas + offset) * scale - * - * @param point - Point in canvas coordinate system - * @returns Point in screen coordinate system - */ - const canvasToScreen = (point: Point): Point => { - return { - x: (point.x + camera.x) * camera.z, - y: (point.y + camera.y) * camera.z - } - } - - /** - * Converts screen coordinates to canvas coordinates - * - * Inverse of canvasToScreen. Useful for hit testing and converting - * mouse events back to canvas space. - * - * Formula: canvas = screen / scale - offset - * - * @param point - Point in screen coordinate system - * @returns Point in canvas coordinate system - */ - const screenToCanvas = (point: Point): Point => { - return { - x: point.x / camera.z - camera.x, - y: point.y / camera.z - camera.y - } - } - - // Get node's screen bounds for culling - const getNodeScreenBounds = ( - pos: ArrayLike, - size: ArrayLike - ): DOMRect => { - const topLeft = canvasToScreen({ x: pos[0], y: pos[1] }) - const width = size[0] * camera.z - const height = size[1] * camera.z - - return new DOMRect(topLeft.x, topLeft.y, width, height) - } - - // Helper: Calculate zoom-adjusted margin for viewport culling - const calculateAdjustedMargin = (baseMargin: number): number => { - if (camera.z < 0.1) return Math.min(baseMargin * 5, 2.0) - if (camera.z > 3.0) return Math.max(baseMargin * 0.5, 0.05) - return baseMargin - } - - // Helper: Check if node is too small to be visible at current zoom - const isNodeTooSmall = (nodeSize: ArrayLike): boolean => { - const nodeScreenSize = Math.max(nodeSize[0], nodeSize[1]) * camera.z - return nodeScreenSize < 4 - } - - // Helper: Calculate expanded viewport bounds with margin - const getExpandedViewportBounds = ( - viewport: { width: number; height: number }, - margin: number - ) => { - const marginX = viewport.width * margin - const marginY = viewport.height * margin - return { - left: -marginX, - right: viewport.width + marginX, - top: -marginY, - bottom: viewport.height + marginY - } - } - - // Helper: Test if node intersects with viewport bounds - const testViewportIntersection = ( - screenPos: { x: number; y: number }, - nodeSize: ArrayLike, - bounds: { left: number; right: number; top: number; bottom: number } - ): boolean => { - const nodeRight = screenPos.x + nodeSize[0] * camera.z - const nodeBottom = screenPos.y + nodeSize[1] * camera.z - - return !( - nodeRight < bounds.left || - screenPos.x > bounds.right || - nodeBottom < bounds.top || - screenPos.y > bounds.bottom - ) - } - - // Check if node is within viewport with frustum and size-based culling - const isNodeInViewport = ( - nodePos: ArrayLike, - nodeSize: ArrayLike, - viewport: { width: number; height: number }, - margin: number = 0.2 - ): boolean => { - // Early exit for tiny nodes - if (isNodeTooSmall(nodeSize)) return false - - const screenPos = canvasToScreen({ x: nodePos[0], y: nodePos[1] }) - const adjustedMargin = calculateAdjustedMargin(margin) - const bounds = getExpandedViewportBounds(viewport, adjustedMargin) - - return testViewportIntersection(screenPos, nodeSize, bounds) - } - - // Get viewport bounds in canvas coordinates (for spatial index queries) - const getViewportBounds = ( - viewport: { width: number; height: number }, - margin: number = 0.2 - ) => { - const marginX = viewport.width * margin - const marginY = viewport.height * margin - - const topLeft = screenToCanvas({ x: -marginX, y: -marginY }) - const bottomRight = screenToCanvas({ - x: viewport.width + marginX, - y: viewport.height + marginY - }) - - return { - x: topLeft.x, - y: topLeft.y, - width: bottomRight.x - topLeft.x, - height: bottomRight.y - topLeft.y - } - } - - return { - camera: readonly(camera), - transformStyle, - syncWithCanvas, - canvasToScreen, - screenToCanvas, - getNodeScreenBounds, - isNodeInViewport, - getViewportBounds - } -}