@schlenkr/quassel 0.5.3 → 0.5.4

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/chat-scroll.d.ts CHANGED
@@ -3,7 +3,8 @@ interface ScrollViewport {
3
3
  readonly scrollHeight: number;
4
4
  readonly clientHeight: number;
5
5
  }
6
- export declare function createChatScroll(onFollowChange?: (following: boolean) => void): {
6
+ export declare const defaultJumpToEndThreshold = 120;
7
+ export declare function createChatScroll(onFollowChange?: (following: boolean) => void, getThreshold?: () => number): {
7
8
  layout: (viewport: ScrollViewport, dragging?: boolean) => void;
8
9
  pause(viewport: ScrollViewport): void;
9
10
  scroll(viewport: ScrollViewport, dragging?: boolean): void;
package/index.js CHANGED
@@ -170,9 +170,10 @@ import { createPortal } from "react-dom";
170
170
  import { Dialog } from "@base-ui/react/dialog";
171
171
 
172
172
  // packages/quassel/src/chat-scroll.ts
173
- var atEnd = (viewport) => viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop <= 24;
173
+ var defaultJumpToEndThreshold = 120;
174
174
  function createChatScroll(onFollowChange = () => {
175
- }) {
175
+ }, getThreshold = () => defaultJumpToEndThreshold) {
176
+ const atEnd = (viewport) => viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop <= getThreshold();
176
177
  let following = true;
177
178
  let lastTop = 0;
178
179
  let lastHeight = 0;
@@ -191,18 +192,18 @@ function createChatScroll(onFollowChange = () => {
191
192
  const layout = (viewport, dragging = false) => {
192
193
  if (viewport.clientHeight <= 0) return;
193
194
  if (movedUp(viewport, dragging) && !atEnd(viewport)) follow(false);
195
+ if (atEnd(viewport)) follow(true);
194
196
  if (following) viewport.scrollTop = Math.max(0, viewport.scrollHeight - viewport.clientHeight);
195
- else if (viewport.scrollHeight <= viewport.clientHeight) follow(true);
196
197
  remember(viewport);
197
198
  };
198
199
  return {
199
200
  layout,
200
201
  pause(viewport) {
201
- if (viewport.scrollHeight > viewport.clientHeight) follow(false);
202
+ if (viewport.clientHeight > 0) follow(atEnd(viewport));
202
203
  },
203
204
  scroll(viewport, dragging = false) {
204
205
  if (viewport.clientHeight <= 0) return;
205
- if (atEnd(viewport) && viewport.scrollTop > lastTop) follow(true);
206
+ if (atEnd(viewport)) follow(true);
206
207
  else if (movedUp(viewport, dragging) && !atEnd(viewport)) follow(false);
207
208
  remember(viewport);
208
209
  },
@@ -1223,7 +1224,7 @@ function ChatMessages({
1223
1224
  scrollerRef,
1224
1225
  maxWidth,
1225
1226
  horizontalPadding,
1226
- jumpToEndThreshold = 120,
1227
+ jumpToEndThreshold = defaultJumpToEndThreshold,
1227
1228
  appearance,
1228
1229
  timestampOptions,
1229
1230
  codeBlockOptions,
@@ -1253,7 +1254,11 @@ function ChatMessages({
1253
1254
  const scrollbarDragging = useRef3(false);
1254
1255
  const touchY = useRef3(void 0);
1255
1256
  const [showJumpToEnd, setShowJumpToEnd] = useState4(false);
1256
- const [scroll] = useState4(() => createChatScroll());
1257
+ const endThreshold = useRef3(jumpToEndThreshold);
1258
+ useLayoutEffect(() => {
1259
+ endThreshold.current = jumpToEndThreshold;
1260
+ }, [jumpToEndThreshold]);
1261
+ const [scroll] = useState4(() => createChatScroll((following) => setShowJumpToEnd(!following), () => endThreshold.current));
1257
1262
  const sendScope = useContext3(ChatSendContext);
1258
1263
  const dock = useContext3(ChatActionDock);
1259
1264
  const [now, setNow] = useState4(() => Date.now());
@@ -1275,15 +1280,11 @@ function ChatMessages({
1275
1280
  }
1276
1281
  due.filter((message) => selected.includes(message)).map((message) => announce2 ? announce2({ kind: message.role === "action" ? "action" : "reply", message }) : markdownPlainText(message.text)).forEach((text) => text && announce(text));
1277
1282
  }, [messages, selected, running2, announce2]);
1278
- const updateJumpVisibility = useCallback2((viewport) => {
1279
- if (viewport.clientHeight > 0) setShowJumpToEnd(viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop > jumpToEndThreshold);
1280
- }, [jumpToEndThreshold]);
1281
1283
  useLayoutEffect(() => sendScope?.subscribe(() => {
1282
1284
  const viewport = scrollArea.current;
1283
1285
  if (!viewport) return;
1284
1286
  scroll.jump(viewport);
1285
- updateJumpVisibility(viewport);
1286
- }), [sendScope, scroll, updateJumpVisibility]);
1287
+ }), [sendScope, scroll]);
1287
1288
  const pauseUp = (viewport, target) => {
1288
1289
  let element = target instanceof Element ? target : null;
1289
1290
  while (element && element !== viewport) {
@@ -1296,9 +1297,8 @@ function ChatMessages({
1296
1297
  const area = scrollArea.current;
1297
1298
  if (area) {
1298
1299
  scroll.scroll(area, scrollbarDragging.current);
1299
- updateJumpVisibility(area);
1300
1300
  }
1301
- }, [scroll, updateJumpVisibility]);
1301
+ }, [scroll]);
1302
1302
  useEffect3(() => {
1303
1303
  const finishDrag = () => {
1304
1304
  if (scrollbarDragging.current && scrollArea.current) scroll.scroll(scrollArea.current, true);
@@ -1316,7 +1316,6 @@ function ChatMessages({
1316
1316
  if (!area) return;
1317
1317
  const align = () => {
1318
1318
  scroll.layout(area, scrollbarDragging.current);
1319
- updateJumpVisibility(area);
1320
1319
  };
1321
1320
  align();
1322
1321
  const observer = new ResizeObserver(align);
@@ -1520,7 +1519,6 @@ function ChatMessages({
1520
1519
  showJumpToEnd && /* @__PURE__ */ jsx13("div", { className: "qsl:pointer-events-none qsl:absolute qsl:inset-x-0 qsl:bottom-[calc(var(--qsl-composer-height,0px)_+_12px)] qsl:z-10 qsl:flex qsl:justify-center", children: /* @__PURE__ */ jsx13(Button2, { "aria-label": allTexts.jumpToEnd, className: "qsl:pointer-events-auto qsl:rounded-full qsl:border qsl:border-primary-foreground/20 qsl:shadow-md", size: "icon", title: allTexts.jumpToEnd, variant: "default", onClick: () => {
1521
1520
  if (scrollArea.current) {
1522
1521
  scroll.jump(scrollArea.current);
1523
- updateJumpVisibility(scrollArea.current);
1524
1522
  }
1525
1523
  }, children: /* @__PURE__ */ jsx13(ChevronDownIcon, {}) }) }),
1526
1524
  dock && openActions.length > 0 && createPortal(