mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(embedded): autolayout viewport calculation for resource view (#3629)
* fix(embedded): autolayout viewport calculation for resource view * fix(embedded): default mothership view to 60% width, remove minimum
This commit is contained in:
+2
-2
@@ -60,11 +60,11 @@ export const MothershipView = memo(function MothershipView({
|
||||
<div
|
||||
className={cn(
|
||||
'relative z-10 flex h-full flex-col overflow-hidden border-[var(--border)] bg-[var(--bg)] transition-[width,min-width,border-width] duration-300 ease-out',
|
||||
isCollapsed ? 'w-0 min-w-0 border-l-0' : 'w-[50%] min-w-[400px] border-l',
|
||||
isCollapsed ? 'w-0 min-w-0 border-l-0' : 'w-[60%] border-l',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className='flex min-h-0 min-w-[400px] flex-1 flex-col'>
|
||||
<div className='flex min-h-0 flex-1 flex-col'>
|
||||
<ResourceTabs
|
||||
workspaceId={workspaceId}
|
||||
chatId={chatId}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useReactFlow } from 'reactflow'
|
||||
import type { AutoLayoutOptions } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils'
|
||||
import { applyAutoLayoutAndUpdateStore as applyAutoLayoutStandalone } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils'
|
||||
import { useSnapToGridSize } from '@/hooks/queries/general-settings'
|
||||
import type { CanvasViewportOptions } from '@/hooks/use-canvas-viewport'
|
||||
import { useCanvasViewport } from '@/hooks/use-canvas-viewport'
|
||||
|
||||
export type { AutoLayoutOptions }
|
||||
@@ -18,9 +19,9 @@ const logger = createLogger('useAutoLayout')
|
||||
*
|
||||
* Note: This hook requires a ReactFlowProvider ancestor.
|
||||
*/
|
||||
export function useAutoLayout(workflowId: string | null) {
|
||||
export function useAutoLayout(workflowId: string | null, options?: CanvasViewportOptions) {
|
||||
const reactFlowInstance = useReactFlow()
|
||||
const { fitViewToBounds } = useCanvasViewport(reactFlowInstance)
|
||||
const { fitViewToBounds } = useCanvasViewport(reactFlowInstance, options)
|
||||
const snapToGridSize = useSnapToGridSize()
|
||||
|
||||
const applyAutoLayoutAndUpdateStore = useCallback(
|
||||
|
||||
@@ -264,7 +264,9 @@ const WorkflowContent = React.memo(
|
||||
const router = useRouter()
|
||||
const reactFlowInstance = useReactFlow()
|
||||
const { screenToFlowPosition, getNodes, setNodes, getIntersectingNodes } = reactFlowInstance
|
||||
const { fitViewToBounds, getViewportCenter } = useCanvasViewport(reactFlowInstance)
|
||||
const { fitViewToBounds, getViewportCenter } = useCanvasViewport(reactFlowInstance, {
|
||||
embedded,
|
||||
})
|
||||
const { emitCursorUpdate } = useSocket()
|
||||
useDynamicHandleRefresh()
|
||||
|
||||
@@ -473,7 +475,9 @@ const WorkflowContent = React.memo(
|
||||
[]
|
||||
)
|
||||
|
||||
const { handleAutoLayout: autoLayoutWithFitView } = useAutoLayout(activeWorkflowId || null)
|
||||
const { handleAutoLayout: autoLayoutWithFitView } = useAutoLayout(activeWorkflowId || null, {
|
||||
embedded,
|
||||
})
|
||||
|
||||
const isWorkflowEmpty = useMemo(() => Object.keys(blocks).length === 0, [blocks])
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import type { Node, ReactFlowInstance } from 'reactflow'
|
||||
import { BLOCK_DIMENSIONS } from '@/lib/workflows/blocks/block-dimensions'
|
||||
|
||||
@@ -10,18 +10,34 @@ interface VisibleBounds {
|
||||
offsetBottom: number
|
||||
}
|
||||
|
||||
export interface CanvasViewportOptions {
|
||||
embedded?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visible canvas bounds accounting for sidebar, terminal, and panel overlays.
|
||||
* Works correctly regardless of whether the ReactFlow container extends under the sidebar or not.
|
||||
* When embedded, uses the container rect directly since CSS variable offsets don't apply.
|
||||
*/
|
||||
function getVisibleCanvasBounds(): VisibleBounds {
|
||||
function getVisibleCanvasBounds(options?: CanvasViewportOptions): VisibleBounds {
|
||||
const flowContainer = document.querySelector('.react-flow')
|
||||
|
||||
if (options?.embedded && flowContainer) {
|
||||
const rect = flowContainer.getBoundingClientRect()
|
||||
return {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
offsetLeft: 0,
|
||||
offsetRight: 0,
|
||||
offsetBottom: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const style = getComputedStyle(document.documentElement)
|
||||
|
||||
const sidebarWidth = Number.parseInt(style.getPropertyValue('--sidebar-width') || '0', 10)
|
||||
const terminalHeight = Number.parseInt(style.getPropertyValue('--terminal-height') || '0', 10)
|
||||
const panelWidth = Number.parseInt(style.getPropertyValue('--panel-width') || '0', 10)
|
||||
|
||||
const flowContainer = document.querySelector('.react-flow')
|
||||
if (!flowContainer) {
|
||||
return {
|
||||
width: window.innerWidth - sidebarWidth - panelWidth,
|
||||
@@ -56,8 +72,8 @@ function getVisibleCanvasBounds(): VisibleBounds {
|
||||
/**
|
||||
* Gets the center of the visible canvas in screen coordinates.
|
||||
*/
|
||||
function getVisibleCanvasCenter(): { x: number; y: number } {
|
||||
const bounds = getVisibleCanvasBounds()
|
||||
function getVisibleCanvasCenter(options?: CanvasViewportOptions): { x: number; y: number } {
|
||||
const bounds = getVisibleCanvasBounds(options)
|
||||
|
||||
const flowContainer = document.querySelector('.react-flow')
|
||||
const rect = flowContainer?.getBoundingClientRect()
|
||||
@@ -81,7 +97,16 @@ interface FitViewToBoundsOptions {
|
||||
/**
|
||||
* Hook providing canvas viewport utilities that account for sidebar, panel, and terminal overlays.
|
||||
*/
|
||||
export function useCanvasViewport(reactFlowInstance: ReactFlowInstance | null) {
|
||||
export function useCanvasViewport(
|
||||
reactFlowInstance: ReactFlowInstance | null,
|
||||
viewportOptions?: CanvasViewportOptions
|
||||
) {
|
||||
const embedded = viewportOptions?.embedded
|
||||
const stableOptions = useMemo<CanvasViewportOptions | undefined>(
|
||||
() => (embedded ? { embedded } : undefined),
|
||||
[embedded]
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets the center of the visible canvas in flow coordinates.
|
||||
*/
|
||||
@@ -90,9 +115,9 @@ export function useCanvasViewport(reactFlowInstance: ReactFlowInstance | null) {
|
||||
return { x: 0, y: 0 }
|
||||
}
|
||||
|
||||
const center = getVisibleCanvasCenter()
|
||||
const center = getVisibleCanvasCenter(stableOptions)
|
||||
return reactFlowInstance.screenToFlowPosition(center)
|
||||
}, [reactFlowInstance])
|
||||
}, [reactFlowInstance, stableOptions])
|
||||
|
||||
/**
|
||||
* Fits the view to show all nodes within the visible canvas bounds,
|
||||
@@ -116,7 +141,7 @@ export function useCanvasViewport(reactFlowInstance: ReactFlowInstance | null) {
|
||||
return
|
||||
}
|
||||
|
||||
const bounds = getVisibleCanvasBounds()
|
||||
const bounds = getVisibleCanvasBounds(stableOptions)
|
||||
|
||||
// Calculate node bounds
|
||||
let minX = Number.POSITIVE_INFINITY
|
||||
@@ -160,12 +185,11 @@ export function useCanvasViewport(reactFlowInstance: ReactFlowInstance | null) {
|
||||
|
||||
reactFlowInstance.setViewport({ x, y, zoom }, { duration })
|
||||
},
|
||||
[reactFlowInstance]
|
||||
[reactFlowInstance, stableOptions]
|
||||
)
|
||||
|
||||
return {
|
||||
getViewportCenter,
|
||||
fitViewToBounds,
|
||||
getVisibleCanvasBounds,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user