Skip to content

Commit b55079e

Browse files
committed
works with tab resize now
1 parent c36391f commit b55079e

File tree

8 files changed

+135
-115
lines changed

8 files changed

+135
-115
lines changed

gui/src/components/TabBar/TabBar.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const TabBarContainer = styled.div`
3535
border-bottom: none;
3636
position: relative;
3737
margin-top: 2px;
38+
max-height: 100px;
39+
overflow: auto;
3840
3941
/* Hide scrollbar but keep functionality */
4042
scrollbar-width: none;
@@ -223,10 +225,13 @@ export const TabBar = React.forwardRef<HTMLDivElement>((_, ref) => {
223225
}
224226
};
225227

226-
return tabs.length === 1 ? (
227-
<></>
228-
) : (
229-
<TabBarContainer ref={ref}>
228+
return (
229+
<TabBarContainer
230+
ref={ref}
231+
style={{
232+
display: tabs.length === 1 ? "none" : "flex",
233+
}}
234+
>
230235
{tabs.map((tab) => (
231236
<Tab
232237
key={tab.id}

gui/src/components/find/FindWidget.tsx

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@ import {
33
ArrowUpIcon,
44
XMarkIcon,
55
} from "@heroicons/react/24/outline";
6-
import { RefObject, useCallback, useEffect, useMemo, useState } from "react";
6+
import {
7+
RefObject,
8+
useCallback,
9+
useEffect,
10+
useMemo,
11+
useRef,
12+
useState,
13+
} from "react";
714
import { HeaderButton, Input } from "..";
815
import HeaderButtonWithToolTip from "../gui/HeaderButtonWithToolTip";
916
import {
1017
Rectangle,
1118
SearchMatch,
1219
searchWithinContainer,
1320
} from "./findWidgetSearch";
14-
import { useDebouncedInput } from "./useDebouncedInput";
15-
import { useElementSize } from "./useElementDims";
21+
import useDebounceValue from "./useDebounce";
22+
import { useElementSize } from "./useElementSize";
1623

1724
interface HighlightOverlayProps {
1825
rectangle: Rectangle;
@@ -51,15 +58,12 @@ type ScrollToMatchOption = "closest" | "first" | "none";
5158
export const useFindWidget = (
5259
searchRef: RefObject<HTMLDivElement>,
5360
headerRef: RefObject<HTMLDivElement>,
54-
refreshDependencies: React.DependencyList,
5561
disabled: boolean,
5662
) => {
5763
// Search input, debounced
58-
const {
59-
inputRef,
60-
debouncedValue: searchTerm,
61-
currentValue,
62-
} = useDebouncedInput();
64+
const inputRef = useRef<HTMLInputElement>(null);
65+
const [currentValue, setCurrentValue] = useState<string>("");
66+
const searchTerm = useDebounceValue(currentValue, 300);
6367

6468
// Widget open/closed state
6569
const [open, setOpen] = useState<boolean>(false);
@@ -134,14 +138,12 @@ export const useFindWidget = (
134138
}, [inputRef, matches, nextMatch]);
135139

136140
// Handle container resize changes - highlight positions must adjust
137-
const headerSize = useElementSize(headerRef);
138-
const containerSize = useElementSize(searchRef);
141+
const { clientHeight: headerHeight, isResizing: headerResizing } =
142+
useElementSize(headerRef);
143+
const { isResizing: containerResizing } = useElementSize(searchRef);
139144
const isResizing = useMemo(() => {
140-
return containerSize.isResizing || headerSize.isResizing;
141-
}, [containerSize, headerSize]);
142-
const headerHeight = useMemo(() => {
143-
return headerSize.size.height;
144-
}, [headerSize]);
145+
return containerResizing || headerResizing;
146+
}, [containerResizing, headerResizing]);
145147

146148
// Main function for finding matches and generating highlight overlays
147149
const refreshSearch = useCallback(
@@ -192,7 +194,7 @@ export const useFindWidget = (
192194
} else {
193195
refreshSearch("closest");
194196
}
195-
}, [refreshSearch, open, disabled, isResizing, ...refreshDependencies]);
197+
}, [refreshSearch, open, disabled, isResizing]);
196198

197199
// Clicks in search div can cause content changes that for some reason don't trigger resize
198200
// Refresh clicking within container
@@ -220,6 +222,9 @@ export const useFindWidget = (
220222
type="text"
221223
ref={inputRef}
222224
value={currentValue}
225+
onChange={(e) => {
226+
setCurrentValue(e.target.value);
227+
}}
223228
placeholder="Search..."
224229
/>
225230
<p className="xs:block hidden min-w-12 whitespace-nowrap px-1 text-center text-xs">

gui/src/components/find/findWidgetSearch.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ export const searchWithinContainer = (
8787
startIndex = endIndex;
8888

8989
const top =
90-
rect.top + searchContainer.clientTop + searchContainer.scrollTop;
91-
// -
92-
// options.offsetHeight;
90+
rect.top +
91+
searchContainer.clientTop +
92+
searchContainer.scrollTop -
93+
options.offsetHeight;
9394

9495
const left =
9596
rect.left + searchContainer.clientLeft + searchContainer.scrollLeft;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useEffect, useState } from "react";
2+
3+
function useDebounceValue<T>(value: T, delay: number = 500): T {
4+
const [debouncedValue, setDebouncedValue] = useState<T>(value);
5+
6+
useEffect(() => {
7+
const timer = setTimeout(() => {
8+
setDebouncedValue(value);
9+
}, delay);
10+
11+
return () => {
12+
clearTimeout(timer);
13+
};
14+
}, [value, delay]);
15+
16+
return debouncedValue;
17+
}
18+
19+
export default useDebounceValue;

gui/src/components/find/useDebouncedInput.ts

Lines changed: 0 additions & 48 deletions
This file was deleted.

gui/src/components/find/useElementDims.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { RefObject, useCallback, useEffect, useState } from "react";
2+
3+
interface ElementSize {
4+
clientWidth: number;
5+
clientHeight: number;
6+
scrollWidth: number;
7+
scrollHeight: number;
8+
isResizing: boolean;
9+
}
10+
11+
interface UseElementSizeOptions {
12+
debounceMs?: number;
13+
}
14+
15+
export const useElementSize = (
16+
ref: RefObject<HTMLElement>,
17+
debounceMs = 250,
18+
): ElementSize => {
19+
const [size, setSize] = useState<ElementSize>({
20+
clientWidth: 0,
21+
clientHeight: 0,
22+
scrollWidth: 0,
23+
scrollHeight: 0,
24+
isResizing: false,
25+
});
26+
27+
// Debounced update function
28+
const debouncedSetSize = useCallback(
29+
(measurements: Omit<ElementSize, "isResizing">) => {
30+
let timeoutId: NodeJS.Timeout;
31+
32+
setSize((prev) => ({ ...prev, isResizing: true }));
33+
34+
timeoutId = setTimeout(() => {
35+
setSize({
36+
...measurements,
37+
isResizing: false,
38+
});
39+
}, debounceMs);
40+
41+
return () => clearTimeout(timeoutId);
42+
},
43+
[debounceMs],
44+
);
45+
46+
useEffect(() => {
47+
if (!ref.current) return;
48+
49+
const updateSize = () => {
50+
const element = ref.current;
51+
if (!element) return;
52+
53+
const measurements = {
54+
clientWidth: element.clientWidth,
55+
clientHeight: element.clientHeight,
56+
scrollWidth: element.scrollWidth,
57+
scrollHeight: element.scrollHeight,
58+
};
59+
60+
debouncedSetSize(measurements);
61+
};
62+
63+
// Create ResizeObserver instance
64+
const resizeObserver = new ResizeObserver(() => {
65+
updateSize();
66+
});
67+
68+
// Start observing the element
69+
resizeObserver.observe(ref.current);
70+
71+
// Initial size calculation
72+
updateSize();
73+
74+
// Cleanup
75+
return () => {
76+
resizeObserver.disconnect();
77+
};
78+
}, [ref, debouncedSetSize]);
79+
80+
return size;
81+
};

gui/src/pages/gui/Chat.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ export function Chat() {
156156
const { widget, highlights } = useFindWidget(
157157
stepsDivRef,
158158
tabsRef,
159-
[history],
160159
isStreaming,
161160
);
162161

0 commit comments

Comments
 (0)