Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/sim/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
}

// Sidebar width. Mirror getMaxSidebarWidth() in stores/sidebar/store.ts:
// 30% of the viewport capped at 400px, and never below the 256px
// 30% of the viewport capped at 400px, and never below the 224px
// minimum, so a narrow window yields a width >= MIN instead of a
// sub-minimum sliver.
var defaultSidebarWidth = 256;
Expand Down Expand Up @@ -149,11 +149,11 @@ export default function RootLayout({ children }: { children: React.ReactNode })
// collapsed, because the desktop hover-peek renders the sidebar at
// its restore width while --sidebar-width still reads collapsed.
var width = state && state.sidebarWidth;
var maxSidebarWidth = Math.max(256, Math.min(400, window.innerWidth * 0.3));
var maxSidebarWidth = Math.max(224, Math.min(400, window.innerWidth * 0.3));
var expandedWidth =
typeof width === 'number' && isFinite(width)
? Math.min(Math.max(width, 256), maxSidebarWidth)
: defaultSidebarWidth;
? Math.min(Math.max(width, 224), maxSidebarWidth)
: Math.min(defaultSidebarWidth, maxSidebarWidth);
document.documentElement.style.setProperty(
'--sidebar-expanded-width',
expandedWidth + 'px'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ export function Avatars({ workflowId }: AvatarsProps) {

/**
* Scale the max visible avatars between MIN_COUNT and MAX_COUNT as the sidebar
* widens.
* widens past its default width.
*/
const maxVisible = useMemo(() => {
const widthDelta = sidebarWidth - SIDEBAR_WIDTH.MIN
const widthDelta = sidebarWidth - SIDEBAR_WIDTH.DEFAULT
const additionalAvatars = Math.floor(widthDelta / AVATAR_CONFIG.WIDTH_PER_AVATAR)
const calculated = AVATAR_CONFIG.MIN_COUNT + additionalAvatars
return Math.max(AVATAR_CONFIG.MIN_COUNT, Math.min(AVATAR_CONFIG.MAX_COUNT, calculated))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ export const Sidebar = memo(function Sidebar() {

const handleOpenSettings = (section: SettingsSection) => {
if (!isCollapsedRef.current) {
setSidebarWidth(SIDEBAR_WIDTH.MIN)
setSidebarWidth(SIDEBAR_WIDTH.DEFAULT)
}
navigateToSettings({ section })
}
Expand Down Expand Up @@ -898,7 +898,7 @@ export const Sidebar = memo(function Sidebar() {
const navigateToPage = useCallback(
(path: string) => {
if (!isCollapsedRef.current) {
setSidebarWidth(SIDEBAR_WIDTH.MIN)
setSidebarWidth(SIDEBAR_WIDTH.DEFAULT)
}
router.push(path)
},
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/stores/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const CONTENT_WINDOW_GAP = 0
/** Sidebar width constraints */
export const SIDEBAR_WIDTH = {
DEFAULT: 256,
MIN: 256,
/** Narrowest the expanded rail can be dragged — slightly under the default */
MIN: 224,
/** Width when sidebar is collapsed to icon-only mode */
COLLAPSED: 48,
/**
Expand Down
24 changes: 24 additions & 0 deletions apps/sim/stores/sidebar/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ describe('sidebar width CSS variables', () => {
expect(widthVars()).toEqual({ width: '300px', expanded: '300px' })
})

it('allows narrowing below the default down to the minimum', () => {
useSidebarStore.getState().setSidebarWidth(SIDEBAR_WIDTH.MIN)
expect(useSidebarStore.getState().sidebarWidth).toBe(SIDEBAR_WIDTH.MIN)
expect(SIDEBAR_WIDTH.MIN).toBeLessThan(SIDEBAR_WIDTH.DEFAULT)

useSidebarStore.getState().setSidebarWidth(SIDEBAR_WIDTH.MIN - 1)
expect(useSidebarStore.getState().sidebarWidth).toBe(SIDEBAR_WIDTH.MIN)
})

it('keeps the expanded variable at the restore width while collapsed', () => {
useSidebarStore.getState().setSidebarWidth(300)
useSidebarStore.getState().toggleCollapsed()
Expand Down Expand Up @@ -93,6 +102,21 @@ describe('sidebar width CSS variables', () => {

expect(widthVars().expanded).toBe(`${SIDEBAR_WIDTH.MIN}px`)
})

it('clamps the default fallback to a viewport maximum below the default', () => {
const innerWidth = window.innerWidth
window.innerWidth = 800
try {
useSidebarStore.setState({ isCollapsed: false, sidebarWidth: Number.NaN })

useSidebarStore.getState().syncWidth()

expect(widthVars().expanded).toBe(`${getMaxSidebarWidth(800)}px`)
expect(getMaxSidebarWidth(800)).toBeLessThan(SIDEBAR_WIDTH.DEFAULT)
} finally {
window.innerWidth = innerWidth
}
})
})

describe('getMaxSidebarWidth', () => {
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/stores/sidebar/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ export function getMaxSidebarWidth(viewportWidth: number): number {

/** Clamps an expanded sidebar width into the valid range for the current viewport. */
function clampSidebarWidth(width: number): number {
if (!Number.isFinite(width)) return SIDEBAR_WIDTH.DEFAULT
const target = Number.isFinite(width) ? width : SIDEBAR_WIDTH.DEFAULT
const max =
typeof window === 'undefined' ? Number.POSITIVE_INFINITY : getMaxSidebarWidth(window.innerWidth)
return Math.min(Math.max(width, SIDEBAR_WIDTH.MIN), max)
return Math.min(Math.max(target, SIDEBAR_WIDTH.MIN), max)
}

/**
Expand Down
Loading