diff --git a/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.stories.tsx b/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.stories.tsx
index 777291698..2a23050e7 100644
--- a/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.stories.tsx
+++ b/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.stories.tsx
@@ -77,7 +77,9 @@ export const LeftPanelPreserved: Story = {
rightChildren: ,
autoSaveId: 'left-preserved',
defaultLayout: ['300px', 'auto'],
+ initialLayout: ['100px', 'auto'],
preserveSide: 'left',
+ leftPanelMinSize: 200,
},
};
@@ -89,6 +91,7 @@ export const RightPanelPreserved: Story = {
autoSaveId: 'right-preserved',
defaultLayout: ['auto', '200px'],
preserveSide: 'right',
+ rightPanelMinSize: 200,
},
};
@@ -216,6 +219,7 @@ export const PercentageBasedSizing: Story = {
rightChildren: ,
autoSaveId: 'percentage-sizing',
defaultLayout: ['30%', 'auto'],
+ leftPanelMinSize: '200px',
preserveSide: 'left',
},
};
diff --git a/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.tsx b/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.tsx
index 48cfa935f..f90506e50 100644
--- a/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.tsx
+++ b/apps/web-tss/src/components/ui/layouts/AppSplitter/AppSplitter.tsx
@@ -40,13 +40,13 @@ interface IAppSplitterProps {
/**
* Initial preserved-side size from cookies (in pixels)
*/
- initialLayout?: number | null;
+ initialLayout?: (`${number}px` | `${number}%` | 'auto' | number)[];
/**
* Default layout configuration as [left, right] sizes
* Can be numbers (pixels), percentages (strings like "50%"), or "auto"
*/
- defaultLayout: (`${number}px` | `${number}&` | 'auto' | number)[];
+ defaultLayout: (`${number}px` | `${number}%` | 'auto' | number)[];
/**
* Minimum size for the left panel
@@ -218,7 +218,12 @@ const AppSplitterWrapper = forwardRef(
({ autoSaveId, style, className, split = 'vertical', ...props }, componentRef) => {
const containerRef = useRef(null);
const isVertical = split === 'vertical';
- const [mounted, setMounted] = useState(!props.bustStorageOnInit);
+ const [mounted, setMounted] = useState(
+ !props.leftPanelMinSize &&
+ !props.rightPanelMinSize &&
+ !props.leftPanelMaxSize &&
+ !props.rightPanelMaxSize
+ );
const splitterAutoSaveId = createAutoSaveId(autoSaveId);
const { splitterAutoSaveId: parentSplitterAutoSaveId } = useAppSplitterContext();
@@ -293,7 +298,6 @@ const AppSplitterBase = forwardRef<
isVertical,
splitterAutoSaveId,
containerRef,
- bustStorageOnInit,
split = 'vertical',
},
ref
@@ -322,23 +326,7 @@ const AppSplitterBase = forwardRef<
// STORAGE MANAGEMENT
// ================================
- const bustStorageOnInitSplitter = (preservedSideValue: number | null) => {
- const refSize =
- split === 'vertical'
- ? containerRef.current?.offsetWidth
- : containerRef.current?.offsetHeight;
- // Don't bust storage if container hasn't been sized yet
- if (!refSize || refSize === 0) {
- // console.warn('AppSplitter: container not sized yet');
- return false;
- }
-
- return typeof bustStorageOnInit === 'function'
- ? bustStorageOnInit(preservedSideValue, refSize)
- : !!bustStorageOnInit;
- };
-
- const defaultValue = () => {
+ const defaultValue = useCallback(() => {
const [leftValue, rightValue] = defaultLayout;
const containerSize =
split === 'vertical'
@@ -354,13 +342,65 @@ const AppSplitterBase = forwardRef<
const preserveValue = preserveSide === 'left' ? leftValue : rightValue;
const result = sizeToPixels(preserveValue, containerSize);
return result;
- };
+ }, [defaultLayout, split, preserveSide]);
+
+ const initialValue = useCallback(() => {
+ if (initialLayout) {
+ const [leftValue, rightValue] = initialLayout;
+ const containerSize =
+ split === 'vertical'
+ ? (containerRef.current?.offsetWidth ?? 0)
+ : (containerRef.current?.offsetHeight ?? 0);
+
+ if (preserveSide === 'left' && leftValue === 'auto') {
+ return containerSize;
+ }
+ if (preserveSide === 'right' && rightValue === 'auto') {
+ return containerSize;
+ }
+ const preserveValue = preserveSide === 'left' ? leftValue : rightValue;
+ const result = sizeToPixels(preserveValue, containerSize);
+
+ // Check if the result is within min/max bounds
+ if (containerSize > 0) {
+ const minSize =
+ preserveSide === 'left'
+ ? sizeToPixels(leftPanelMinSize, containerSize)
+ : sizeToPixels(rightPanelMinSize, containerSize);
+ const maxSize =
+ preserveSide === 'left'
+ ? leftPanelMaxSize
+ ? sizeToPixels(leftPanelMaxSize, containerSize)
+ : containerSize
+ : rightPanelMaxSize
+ ? sizeToPixels(rightPanelMaxSize, containerSize)
+ : containerSize;
+
+ // If the result is outside the min/max bounds, use the default value
+ if (result < minSize || result > maxSize) {
+ return defaultValue();
+ }
+ }
+
+ return result;
+ }
+ return defaultValue();
+ }, [
+ initialLayout,
+ split,
+ preserveSide,
+ leftPanelMinSize,
+ rightPanelMinSize,
+ leftPanelMaxSize,
+ rightPanelMaxSize,
+ defaultValue,
+ ]);
// Load saved layout from cookies
+
const [savedLayout, setSavedLayout] = useCookieState(splitterAutoSaveId, {
defaultValue,
- initialValue: initialLayout ?? undefined,
- bustStorageOnInit: bustStorageOnInitSplitter,
+ initialValue,
});
// ================================
diff --git a/apps/web-tss/src/hooks/useCookieState.tsx b/apps/web-tss/src/hooks/useCookieState.tsx
index b95d98ddc..cd9d19e4a 100644
--- a/apps/web-tss/src/hooks/useCookieState.tsx
+++ b/apps/web-tss/src/hooks/useCookieState.tsx
@@ -2,7 +2,7 @@
import { isServer } from '@tanstack/react-query';
import cookies from 'js-cookie';
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
import { useMemoizedFn } from './useMemoizedFn';
import { useMount } from './useMount';
@@ -25,11 +25,10 @@ interface CookieOptions {
interface Options {
defaultValue?: T | (() => T);
- initialValue?: T;
+ initialValue?: T | (() => T);
serializer?: (value: T) => string;
deserializer?: (value: string) => T;
onError?: (error: unknown) => void;
- bustStorageOnInit?: boolean | ((layout: T) => boolean);
expirationTime?: number;
cookieOptions?: CookieOptions;
}
@@ -73,7 +72,6 @@ export function useCookieState(
serializer = JSON.stringify,
deserializer = JSON.parse,
onError,
- bustStorageOnInit = false,
expirationTime = DEFAULT_EXPIRATION_TIME,
cookieOptions = {},
} = options || {};
@@ -85,14 +83,9 @@ export function useCookieState(
// Get initial value from cookies or use default
const getInitialValue = useMemoizedFn((): T | undefined => {
- // If bustStorageOnInit is true, ignore cookies and use default value
- if (bustStorageOnInit === true) {
- return executeBustStorage();
- }
-
// Prefer explicitly provided initialValue if present
if (initialValue !== undefined) {
- return initialValue;
+ return typeof initialValue === 'function' ? (initialValue as () => T)() : initialValue;
}
try {
@@ -128,10 +121,6 @@ export function useCookieState(
// Data is still valid, deserialize and return the value
const deserializedValue = deserializer(JSON.stringify(storageData.value));
- if (typeof bustStorageOnInit === 'function' && bustStorageOnInit(deserializedValue)) {
- return executeBustStorage();
- }
-
return deserializedValue;
} catch (error) {
onError?.(error);
@@ -146,35 +135,28 @@ export function useCookieState(
setState(getInitialValue());
});
- // Update cookies when state changes
- useEffect(() => {
- try {
- if (state === undefined && !isServer) {
- removeCookie(key, cookieOptions);
- } else {
- // Create storage data with current timestamp
- const storageData: StorageData = {
- value: JSON.parse(serializer(state)),
- timestamp: Date.now(),
- };
- setCookie(key, JSON.stringify(storageData), expirationTime, cookieOptions);
- }
- } catch (error) {
- onError?.(error);
- }
- }, [key, state, serializer, onError, expirationTime, cookieOptions]);
-
// Setter function that handles both direct values and function updates
const setStoredState = useMemoizedFn((value?: SetState) => {
try {
- if (typeof value === 'function') {
- setState((prevState) => {
- const newState = (value as (prevState?: T) => T)(prevState);
- return newState;
- });
- } else {
- setState(value);
- }
+ setState((prevState) => {
+ // Calculate the new state value
+ const newState =
+ typeof value === 'function' ? (value as (prevState?: T) => T)(prevState) : value;
+
+ // Update cookie with the new state
+ if (newState === undefined && !isServer) {
+ removeCookie(key, cookieOptions);
+ } else {
+ // Create storage data with current timestamp
+ const storageData: StorageData = {
+ value: JSON.parse(serializer(newState)),
+ timestamp: Date.now(),
+ };
+ setCookie(key, JSON.stringify(storageData), expirationTime, cookieOptions);
+ }
+
+ return newState;
+ });
} catch (error) {
onError?.(error);
}
diff --git a/apps/web/src/layouts/ChatLayout/ChatLayout/ChatLayout.tsx b/apps/web/src/layouts/ChatLayout/ChatLayout/ChatLayout.tsx
index d9b0553d6..b678e8740 100644
--- a/apps/web/src/layouts/ChatLayout/ChatLayout/ChatLayout.tsx
+++ b/apps/web/src/layouts/ChatLayout/ChatLayout/ChatLayout.tsx
@@ -51,8 +51,8 @@ export const ChatLayout: React.FC = ({ children }) => {
}
- rightChildren={mounted && {children}}
+ leftChildren={mounted && renderLeftPanel && }
+ rightChildren={mounted && renderRightPanel && {children}}
autoSaveId={autoSaveId}
defaultLayout={defaultSplitterLayout}
allowResize={selectedLayout === 'both'}
@@ -61,8 +61,6 @@ export const ChatLayout: React.FC = ({ children }) => {
leftPanelMaxSize={leftPanelMaxSize}
rightPanelMinSize={rightPanelMinSize}
rightPanelMaxSize={rightPanelMaxSize}
- renderLeftPanel={renderLeftPanel}
- renderRightPanel={renderRightPanel}
bustStorageOnInit={bustStorageOnInit}
/>
diff --git a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx
index a73fac0d2..649c37922 100644
--- a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx
+++ b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx
@@ -70,7 +70,7 @@ export const FileContainer: React.FC = ({ children }) => {
}
});
- const rightChildren = useMemo(() => {
+ const rightChildren: React.ReactNode = useMemo(() => {
if (!debouncedSelectedFileViewSecondary) {
return null;
}