MCPcopy Index your code
hub / github.com/codeaashu/claude-code / useUnseenDivider

Function useUnseenDivider

src/components/FullscreenLayout.tsx:86–190  ·  view source on GitHub ↗
(messageCount: number)

Source from the content-addressed store, hash-verified

84 * handle; `onRepin` by submit/scroll-to-bottom.
85 */
86export function useUnseenDivider(messageCount: number): {
87 /** Index into messages[] where the divider line renders. Cleared on
88 * sticky-resume (scroll back to bottom) so the "N new" line doesn't
89 * linger once everything is visible. */
90 dividerIndex: number | null;
91 /** scrollHeight snapshot at first scroll-away — the divider's y-position.
92 * FullscreenLayout subscribes to ScrollBox and compares viewport bottom
93 * against this for pillVisible. Ref so writes don't re-render REPL. */
94 dividerYRef: RefObject<number | null>;
95 onScrollAway: (handle: ScrollBoxHandle) => void;
96 onRepin: () => void;
97 /** Scroll the handle so the divider line is at the top of the viewport. */
98 jumpToNew: (handle: ScrollBoxHandle | null) => void;
99 /** Shift dividerIndex and dividerYRef when messages are prepended
100 * (infinite scroll-back). indexDelta = number of messages prepended;
101 * heightDelta = content height growth in rows. */
102 shiftDivider: (indexDelta: number, heightDelta: number) => void;
103} {
104 const [dividerIndex, setDividerIndex] = useState<number | null>(null);
105 // Ref holds the current count for onScrollAway to snapshot. Written in
106 // the render body (not useEffect) so wheel events arriving between a
107 // message-append render and its effect flush don't capture a stale
108 // count (off-by-one in the baseline). React Compiler bails out here —
109 // acceptable for a hook instantiated once in REPL.
110 const countRef = useRef(messageCount);
111 countRef.current = messageCount;
112 // scrollHeight snapshot — the divider's y in content coords. Ref-only:
113 // read synchronously in onScrollAway (setState is batched, can't
114 // read-then-write in the same callback) AND by FullscreenLayout's
115 // pillVisible subscription. null = pinned to bottom.
116 const dividerYRef = useRef<number | null>(null);
117 const onRepin = useCallback(() => {
118 // Don't clear dividerYRef here — a trackpad momentum wheel event
119 // racing in the same stdin batch would see null and re-snapshot,
120 // overriding the setDividerIndex(null) below. The useEffect below
121 // clears the ref after React commits the null dividerIndex, so the
122 // ref stays non-null until the state settles.
123 setDividerIndex(null);
124 }, []);
125 const onScrollAway = useCallback((handle: ScrollBoxHandle) => {
126 // Nothing below the viewport → nothing to jump to. Covers both:
127 // • empty/short session: scrollUp calls scrollTo(0) which breaks sticky
128 // even at scrollTop=0 (wheel-up on fresh session showed the pill)
129 // • click-to-select at bottom: useDragToScroll.check() calls
130 // scrollTo(current) to break sticky so streaming content doesn't shift
131 // under the selection, then onScroll(false, …) — but scrollTop is still
132 // at max (Sarah Deaton, #claude-code-feedback 2026-03-15)
133 // pendingDelta: scrollBy accumulates without updating scrollTop. Without
134 // it, wheeling up from max would see scrollTop==max and suppress the pill.
135 const max = Math.max(0, handle.getScrollHeight() - handle.getViewportHeight());
136 if (handle.getScrollTop() + handle.getPendingDelta() >= max) return;
137 // Snapshot only on the FIRST scroll-away. onScrollAway fires on EVERY
138 // scroll action (not just the initial break from sticky) — this guard
139 // preserves the original baseline so the count doesn't reset on the
140 // second PageUp. Subsequent calls are ref-only no-ops (no REPL re-render).
141 if (dividerYRef.current === null) {
142 dividerYRef.current = handle.getScrollHeight();
143 // New scroll-away session → move the divider here (replaces old one)

Callers 1

REPLFunction · 0.85

Calls 2

maxMethod · 0.80
scrollToBottomMethod · 0.45

Tested by

no test coverage detected