cogsbox-state 0.5.359 → 0.5.361
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CogsState.jsx +742 -726
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +66 -77
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1808,16 +1808,15 @@ function createProxyHandler<T>(
|
|
|
1808
1808
|
stickToBottom = false,
|
|
1809
1809
|
dependencies = [],
|
|
1810
1810
|
} = options;
|
|
1811
|
-
|
|
1811
|
+
|
|
1812
1812
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
1813
1813
|
const [range, setRange] = useState({
|
|
1814
1814
|
startIndex: 0,
|
|
1815
1815
|
endIndex: 10,
|
|
1816
1816
|
});
|
|
1817
1817
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
const scrollLoopId = useRef<NodeJS.Timeout | null>(null);
|
|
1818
|
+
const isAutoScrolling = useRef(false);
|
|
1819
|
+
const prevTotalCountRef = useRef(0);
|
|
1821
1820
|
|
|
1822
1821
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1823
1822
|
|
|
@@ -1858,7 +1857,6 @@ function createProxyHandler<T>(
|
|
|
1858
1857
|
]);
|
|
1859
1858
|
|
|
1860
1859
|
const virtualState = useMemo(() => {
|
|
1861
|
-
// ... same as before ...
|
|
1862
1860
|
const start = Math.max(0, range.startIndex);
|
|
1863
1861
|
const end = Math.min(totalCount, range.endIndex);
|
|
1864
1862
|
const validIndices = Array.from(
|
|
@@ -1872,46 +1870,44 @@ function createProxyHandler<T>(
|
|
|
1872
1870
|
});
|
|
1873
1871
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1874
1872
|
|
|
1875
|
-
// ---
|
|
1876
|
-
|
|
1873
|
+
// --- PHASE 1: Detect auto-scroll need and SET THE RANGE ---
|
|
1874
|
+
useLayoutEffect(() => {
|
|
1875
|
+
const hasNewItems = totalCount > prevTotalCountRef.current;
|
|
1876
|
+
if (isLockedToBottomRef.current && hasNewItems) {
|
|
1877
|
+
console.log(
|
|
1878
|
+
"PHASE 1: Auto-scroll needed. Setting range to render the last item."
|
|
1879
|
+
);
|
|
1880
|
+
setRange({
|
|
1881
|
+
startIndex: Math.max(0, totalCount - 10 - overscan),
|
|
1882
|
+
endIndex: totalCount,
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
prevTotalCountRef.current = totalCount;
|
|
1886
|
+
}, [totalCount]);
|
|
1887
|
+
|
|
1888
|
+
// --- PHASE 2: Wait for measurement and SCROLL ---
|
|
1877
1889
|
useLayoutEffect(() => {
|
|
1878
1890
|
const container = containerRef.current;
|
|
1891
|
+
const isRangeAtEnd =
|
|
1892
|
+
range.endIndex === totalCount && totalCount > 0;
|
|
1893
|
+
|
|
1879
1894
|
if (
|
|
1880
1895
|
!container ||
|
|
1881
|
-
!
|
|
1882
|
-
!
|
|
1896
|
+
!isLockedToBottomRef.current ||
|
|
1897
|
+
!isRangeAtEnd
|
|
1883
1898
|
) {
|
|
1884
1899
|
return;
|
|
1885
1900
|
}
|
|
1886
1901
|
|
|
1887
|
-
// If a loop is already running, stop it before starting a new one.
|
|
1888
|
-
if (scrollLoopId.current) {
|
|
1889
|
-
clearInterval(scrollLoopId.current);
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
console.log("ALGORITHM: Starting...");
|
|
1893
|
-
|
|
1894
|
-
// STEP 1: Set range to render the last item.
|
|
1895
|
-
setRange({
|
|
1896
|
-
startIndex: Math.max(0, totalCount - 10 - overscan),
|
|
1897
|
-
endIndex: totalCount,
|
|
1898
|
-
});
|
|
1899
|
-
|
|
1900
|
-
// STEP 2: Start the LOOP.
|
|
1901
1902
|
console.log(
|
|
1902
|
-
"
|
|
1903
|
+
"PHASE 2: Range is at the end. Starting the measurement loop."
|
|
1903
1904
|
);
|
|
1904
1905
|
let loopCount = 0;
|
|
1905
|
-
|
|
1906
|
+
const intervalId = setInterval(() => {
|
|
1906
1907
|
loopCount++;
|
|
1907
1908
|
console.log(`LOOP ${loopCount}: Checking last item...`);
|
|
1908
1909
|
|
|
1909
1910
|
const lastItemIndex = totalCount - 1;
|
|
1910
|
-
if (lastItemIndex < 0) {
|
|
1911
|
-
clearInterval(scrollLoopId.current!);
|
|
1912
|
-
return;
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
1911
|
const shadowArray =
|
|
1916
1912
|
getGlobalStore
|
|
1917
1913
|
.getState()
|
|
@@ -1921,67 +1917,75 @@ function createProxyHandler<T>(
|
|
|
1921
1917
|
|
|
1922
1918
|
if (lastItemHeight > 0) {
|
|
1923
1919
|
console.log(
|
|
1924
|
-
`%cSUCCESS: Last item is
|
|
1920
|
+
`%cSUCCESS: Last item height is ${lastItemHeight}. Scrolling now.`,
|
|
1925
1921
|
"color: green; font-weight: bold;"
|
|
1926
1922
|
);
|
|
1927
|
-
clearInterval(
|
|
1928
|
-
scrollLoopId.current = null;
|
|
1923
|
+
clearInterval(intervalId);
|
|
1929
1924
|
|
|
1925
|
+
isAutoScrolling.current = true;
|
|
1930
1926
|
container.scrollTo({
|
|
1931
1927
|
top: container.scrollHeight,
|
|
1932
1928
|
behavior: "smooth",
|
|
1933
1929
|
});
|
|
1930
|
+
setTimeout(() => {
|
|
1931
|
+
isAutoScrolling.current = false;
|
|
1932
|
+
}, 1000);
|
|
1933
|
+
} else if (loopCount > 20) {
|
|
1934
|
+
console.error(
|
|
1935
|
+
"LOOP TIMEOUT: Last item was never measured. Stopping loop."
|
|
1936
|
+
);
|
|
1937
|
+
clearInterval(intervalId);
|
|
1934
1938
|
} else {
|
|
1935
1939
|
console.log("...WAITING. Height is not ready.");
|
|
1936
|
-
if (loopCount > 30) {
|
|
1937
|
-
console.error("LOOP TIMEOUT. Stopping.");
|
|
1938
|
-
clearInterval(scrollLoopId.current!);
|
|
1939
|
-
scrollLoopId.current = null;
|
|
1940
|
-
}
|
|
1941
1940
|
}
|
|
1942
1941
|
}, 100);
|
|
1943
1942
|
|
|
1944
|
-
return () =>
|
|
1945
|
-
|
|
1946
|
-
clearInterval(scrollLoopId.current);
|
|
1947
|
-
}
|
|
1948
|
-
};
|
|
1949
|
-
}, [totalCount, ...dependencies]);
|
|
1943
|
+
return () => clearInterval(intervalId);
|
|
1944
|
+
}, [range.endIndex, totalCount, positions]);
|
|
1950
1945
|
|
|
1951
|
-
// ---
|
|
1952
|
-
// This handles manual scrolling and resetting when the chat changes.
|
|
1946
|
+
// --- PHASE 3: Handle User Interaction and Resets ---
|
|
1953
1947
|
useEffect(() => {
|
|
1954
1948
|
const container = containerRef.current;
|
|
1955
1949
|
if (!container) return;
|
|
1956
1950
|
|
|
1957
|
-
// When the chat changes (dependencies change), reset the lock.
|
|
1958
1951
|
console.log(
|
|
1959
|
-
"DEPENDENCY CHANGE: Resetting scroll lock
|
|
1960
|
-
stickToBottom
|
|
1952
|
+
"DEPENDENCY CHANGE: Resetting scroll lock and initial view."
|
|
1961
1953
|
);
|
|
1962
1954
|
isLockedToBottomRef.current = stickToBottom;
|
|
1963
1955
|
|
|
1964
1956
|
const updateVirtualRange = () => {
|
|
1965
|
-
|
|
1957
|
+
const { scrollTop, clientHeight } = container;
|
|
1958
|
+
let low = 0,
|
|
1959
|
+
high = totalCount - 1;
|
|
1960
|
+
while (low <= high) {
|
|
1961
|
+
const mid = Math.floor((low + high) / 2);
|
|
1962
|
+
if (positions[mid]! < scrollTop) low = mid + 1;
|
|
1963
|
+
else high = mid - 1;
|
|
1964
|
+
}
|
|
1965
|
+
const startIndex = Math.max(0, high - overscan);
|
|
1966
|
+
let endIndex = startIndex;
|
|
1967
|
+
const visibleEnd = scrollTop + clientHeight;
|
|
1968
|
+
while (
|
|
1969
|
+
endIndex < totalCount &&
|
|
1970
|
+
positions[endIndex]! < visibleEnd
|
|
1971
|
+
) {
|
|
1972
|
+
endIndex++;
|
|
1973
|
+
}
|
|
1974
|
+
setRange({
|
|
1975
|
+
startIndex,
|
|
1976
|
+
endIndex: Math.min(totalCount, endIndex + overscan),
|
|
1977
|
+
});
|
|
1966
1978
|
};
|
|
1967
1979
|
|
|
1968
1980
|
const handleUserScroll = () => {
|
|
1981
|
+
if (isAutoScrolling.current) return;
|
|
1969
1982
|
const isAtBottom =
|
|
1970
1983
|
container.scrollHeight -
|
|
1971
1984
|
container.scrollTop -
|
|
1972
1985
|
container.clientHeight <
|
|
1973
1986
|
1;
|
|
1974
1987
|
if (!isAtBottom) {
|
|
1975
|
-
|
|
1976
|
-
console.log("USER SCROLL: Lock broken.");
|
|
1977
|
-
isLockedToBottomRef.current = false;
|
|
1978
|
-
// If a scroll loop was running, kill it.
|
|
1979
|
-
if (scrollLoopId.current) {
|
|
1980
|
-
clearInterval(scrollLoopId.current);
|
|
1981
|
-
scrollLoopId.current = null;
|
|
1982
|
-
console.log("...Auto-scroll loop terminated by user.");
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1988
|
+
isLockedToBottomRef.current = false;
|
|
1985
1989
|
}
|
|
1986
1990
|
updateVirtualRange();
|
|
1987
1991
|
};
|
|
@@ -1993,26 +1997,12 @@ function createProxyHandler<T>(
|
|
|
1993
1997
|
|
|
1994
1998
|
return () =>
|
|
1995
1999
|
container.removeEventListener("scroll", handleUserScroll);
|
|
1996
|
-
}, [
|
|
1997
|
-
|
|
1998
|
-
console.log(
|
|
1999
|
-
"DEPENDENCY CHANGE: Resetting scroll lock and scrolling to bottom."
|
|
2000
|
-
);
|
|
2001
|
-
if (containerRef.current) {
|
|
2002
|
-
// Reset the lock so the main effect can take over on the next data load.
|
|
2003
|
-
isLockedToBottomRef.current = stickToBottom;
|
|
2004
|
-
// Scroll to the top to show the loading state for the new chat.
|
|
2005
|
-
containerRef.current.scrollTop = 0;
|
|
2006
|
-
// Reset the range
|
|
2007
|
-
setRange({ startIndex: 0, endIndex: 10 });
|
|
2008
|
-
}
|
|
2009
|
-
}, dependencies);
|
|
2000
|
+
}, [...dependencies]);
|
|
2001
|
+
|
|
2010
2002
|
const scrollToBottom = useCallback(
|
|
2011
2003
|
(behavior: ScrollBehavior = "smooth") => {
|
|
2012
2004
|
if (containerRef.current) {
|
|
2013
2005
|
isLockedToBottomRef.current = true;
|
|
2014
|
-
console.log("USER ACTION: Scroll lock ENABLED.");
|
|
2015
|
-
// This is a manual trigger, so we don't need the loop. Just scroll.
|
|
2016
2006
|
containerRef.current.scrollTo({
|
|
2017
2007
|
top: containerRef.current.scrollHeight,
|
|
2018
2008
|
behavior,
|
|
@@ -2026,7 +2016,6 @@ function createProxyHandler<T>(
|
|
|
2026
2016
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
2027
2017
|
if (containerRef.current && positions[index] !== undefined) {
|
|
2028
2018
|
isLockedToBottomRef.current = false;
|
|
2029
|
-
console.log("USER ACTION: Scroll lock DISABLED.");
|
|
2030
2019
|
containerRef.current.scrollTo({
|
|
2031
2020
|
top: positions[index],
|
|
2032
2021
|
behavior,
|