@schlenkr/quassel 0.5.2 → 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 +2 -1
- package/chat.css +8 -5
- package/index.js +21 -23
- package/index.js.map +2 -2
- package/package.json +1 -1
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
|
|
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/chat.css
CHANGED
|
@@ -771,9 +771,9 @@
|
|
|
771
771
|
--tw-leading: 1.625;
|
|
772
772
|
line-height: 1.625;
|
|
773
773
|
}
|
|
774
|
-
.qsl\:leading-\[var\(--qsl-chat-line-height\,1\.
|
|
775
|
-
--tw-leading: var(--qsl-chat-line-height,1.
|
|
776
|
-
line-height: var(--qsl-chat-line-height,1.
|
|
774
|
+
.qsl\:leading-\[var\(--qsl-chat-line-height\,1\.65\)\] {
|
|
775
|
+
--tw-leading: var(--qsl-chat-line-height,1.65);
|
|
776
|
+
line-height: var(--qsl-chat-line-height,1.65);
|
|
777
777
|
}
|
|
778
778
|
.qsl\:leading-normal {
|
|
779
779
|
--tw-leading: var(--qsl-leading-normal);
|
|
@@ -1608,11 +1608,14 @@
|
|
|
1608
1608
|
opacity: 100%;
|
|
1609
1609
|
}
|
|
1610
1610
|
}
|
|
1611
|
+
li + .qsl\:\[li\+\&\]\:mt-1 {
|
|
1612
|
+
margin-top: var(--qsl-spacing);
|
|
1613
|
+
}
|
|
1611
1614
|
li > .qsl\:\[li\>\&\]\:my-0 {
|
|
1612
1615
|
margin-block: 0px;
|
|
1613
1616
|
}
|
|
1614
|
-
p + .qsl\:\[p\+\&\]\:mt-
|
|
1615
|
-
margin-top: calc(var(--qsl-spacing) *
|
|
1617
|
+
p + .qsl\:\[p\+\&\]\:mt-3 {
|
|
1618
|
+
margin-top: calc(var(--qsl-spacing) * 3);
|
|
1616
1619
|
}
|
|
1617
1620
|
pre > .qsl\:\[pre\>\&\]\:bg-transparent {
|
|
1618
1621
|
background-color: transparent;
|
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
|
|
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.
|
|
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)
|
|
206
|
+
if (atEnd(viewport)) follow(true);
|
|
206
207
|
else if (movedUp(viewport, dragging) && !atEnd(viewport)) follow(false);
|
|
207
208
|
remember(viewport);
|
|
208
209
|
},
|
|
@@ -535,10 +536,10 @@ var components = {
|
|
|
535
536
|
h4: ({ children }) => /* @__PURE__ */ jsx8("h4", { className: "qsl:mt-2 qsl:mb-1 qsl:font-medium", children }),
|
|
536
537
|
h5: ({ children }) => /* @__PURE__ */ jsx8("h5", { className: "qsl:mt-2 qsl:mb-1 qsl:font-medium", children }),
|
|
537
538
|
h6: ({ children }) => /* @__PURE__ */ jsx8("h6", { className: "qsl:mt-2 qsl:mb-1 qsl:font-medium", children }),
|
|
538
|
-
p: ({ children }) => /* @__PURE__ */ jsx8("p", { className: "qsl:[p+&]:mt-
|
|
539
|
-
ul: ({ children }) => /* @__PURE__ */ jsx8("ul", { className: "qsl:my-
|
|
540
|
-
ol: ({ children }) => /* @__PURE__ */ jsx8("ol", { className: "qsl:my-
|
|
541
|
-
li: ({ children }) => /* @__PURE__ */ jsx8("li", { className: "qsl:marker:text-muted-foreground qsl:in-data-[tone=on-color]:marker:text-inherit", children }),
|
|
539
|
+
p: ({ children }) => /* @__PURE__ */ jsx8("p", { className: "qsl:[p+&]:mt-3", children }),
|
|
540
|
+
ul: ({ children }) => /* @__PURE__ */ jsx8("ul", { className: "qsl:my-3 qsl:list-disc qsl:pl-6 qsl:[li>&]:my-0", children }),
|
|
541
|
+
ol: ({ children }) => /* @__PURE__ */ jsx8("ol", { className: "qsl:my-3 qsl:list-decimal qsl:pl-6 qsl:[li>&]:my-0", children }),
|
|
542
|
+
li: ({ children }) => /* @__PURE__ */ jsx8("li", { className: "qsl:[li+&]:mt-1 qsl:marker:text-muted-foreground qsl:in-data-[tone=on-color]:marker:text-inherit", children }),
|
|
542
543
|
hr: () => /* @__PURE__ */ jsx8("hr", { className: "qsl:my-3 qsl:border-t qsl:border-border" }),
|
|
543
544
|
img: ({ src, alt }) => /* @__PURE__ */ jsx8("img", { alt, className: "qsl:h-auto qsl:max-w-full", src: typeof src === "string" ? src : void 0 }),
|
|
544
545
|
blockquote: ({ children }) => /* @__PURE__ */ jsx8("blockquote", { className: "qsl:my-1 qsl:border-l-2 qsl:border-border qsl:pl-3 qsl:text-muted-foreground qsl:in-data-[tone=on-color]:border-white/50 qsl:in-data-[tone=on-color]:text-inherit", children }),
|
|
@@ -1190,7 +1191,7 @@ var chipClasses = cn(
|
|
|
1190
1191
|
var chipInteractiveClasses = "qsl:cursor-pointer qsl:hover:border-border qsl:hover:bg-secondary qsl:hover:text-foreground qsl:focus-visible:border-border qsl:focus-visible:bg-secondary qsl:focus-visible:text-foreground qsl:focus-visible:outline-none qsl:data-[state=done]:hover:opacity-100 qsl:data-[state=done]:focus-visible:opacity-100";
|
|
1191
1192
|
var collapseAtBottomFrom = 11;
|
|
1192
1193
|
var maxAnnouncementsAtOnce = 2;
|
|
1193
|
-
var bubbleBodyClasses = "qsl:relative qsl:max-w-[86%] qsl:px-3 qsl:py-2 qsl:leading-[var(--qsl-chat-line-height,1.
|
|
1194
|
+
var bubbleBodyClasses = "qsl:relative qsl:max-w-[86%] qsl:px-3 qsl:py-2 qsl:leading-[var(--qsl-chat-line-height,1.65)] qsl:[overflow-wrap:anywhere] qsl:@max-[620px]/chat:max-w-full";
|
|
1194
1195
|
function isStep(message) {
|
|
1195
1196
|
return !!message && (message.role === "thinking" || message.role === "tool");
|
|
1196
1197
|
}
|
|
@@ -1223,7 +1224,7 @@ function ChatMessages({
|
|
|
1223
1224
|
scrollerRef,
|
|
1224
1225
|
maxWidth,
|
|
1225
1226
|
horizontalPadding,
|
|
1226
|
-
jumpToEndThreshold =
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
@@ -1785,7 +1783,7 @@ function Bubble({
|
|
|
1785
1783
|
/* @__PURE__ */ jsx13(MessageActions, { message, options: messageActions, texts })
|
|
1786
1784
|
] });
|
|
1787
1785
|
const cursor = message.role === "assistant" && message.textCursor ? JSON.stringify(message.textCursor) : void 0;
|
|
1788
|
-
if (!useBubble) return /* @__PURE__ */ jsx13("div", { "data-chat-text-cursor": cursor, className: cn(stepSpacing, "qsl:group qsl:relative qsl:w-full qsl:leading-[var(--qsl-chat-line-height,1.
|
|
1786
|
+
if (!useBubble) return /* @__PURE__ */ jsx13("div", { "data-chat-text-cursor": cursor, className: cn(stepSpacing, "qsl:group qsl:relative qsl:w-full qsl:leading-[var(--qsl-chat-line-height,1.65)] qsl:text-foreground"), "data-message": "answer", children: content });
|
|
1789
1787
|
const fullBackground = message.role === "user" && bubbleOptions?.userBackgroundFullWidth !== false;
|
|
1790
1788
|
const surfaceClasses = message.bubble ? "qsl:rounded-lg" : "qsl:rounded-[var(--qsl-bubble-radius,10px_10px_4px_10px)] qsl:border qsl:border-border qsl:bg-secondary qsl:in-data-[tone=material]:rounded-[var(--qsl-bubble-radius,9px)] qsl:in-data-[tone=material]:border-glass-edge/20 qsl:in-data-[tone=material]:bg-primary/15";
|
|
1791
1789
|
return /* @__PURE__ */ jsxs6(
|
|
@@ -2170,7 +2168,7 @@ function ChatInputToolbar({
|
|
|
2170
2168
|
fileInput.dragging && "qsl:outline-2 qsl:outline-offset-[3px] qsl:outline-dashed qsl:outline-primary"
|
|
2171
2169
|
);
|
|
2172
2170
|
const textareaClasses = cn(
|
|
2173
|
-
"qsl:resize-none qsl:overscroll-contain qsl:border-0 qsl:bg-transparent qsl:text-chat qsl:leading-[var(--qsl-chat-line-height,1.
|
|
2171
|
+
"qsl:resize-none qsl:overscroll-contain qsl:border-0 qsl:bg-transparent qsl:text-chat qsl:leading-[var(--qsl-chat-line-height,1.65)] qsl:text-foreground qsl:placeholder:text-muted-foreground qsl:focus:outline-none",
|
|
2174
2172
|
inline ? "qsl:min-w-0 qsl:flex-1 qsl:px-2.5 qsl:py-2" : toolbar ? "qsl:w-full qsl:min-w-0 qsl:flex-auto qsl:px-2 qsl:py-[3px] qsl:leading-[1.3]" : "qsl:px-3 qsl:pt-3 qsl:pb-2"
|
|
2175
2173
|
);
|
|
2176
2174
|
const toolbarRowClasses = cn(
|