@schlenkr/quassel 0.5.4 → 0.5.5
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/MessageActions.d.ts +5 -2
- package/chat.css +26 -12
- package/index.d.ts +1 -1
- package/index.js +68 -41
- package/index.js.map +2 -2
- package/options.d.ts +1 -0
- package/package.json +1 -1
- package/texts.d.ts +1 -0
- package/useChatAction.d.ts +1 -0
package/MessageActions.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
1
2
|
import type { MessageActionsOptions } from "./options";
|
|
2
|
-
import type
|
|
3
|
+
import { type ChatTexts } from "./texts";
|
|
3
4
|
import type { Message } from "./types";
|
|
4
|
-
export declare function MessageActions({ message, options, texts }: {
|
|
5
|
+
export declare function MessageActions({ message, options, texts, contentRef, side }: {
|
|
5
6
|
message: Message;
|
|
6
7
|
options?: MessageActionsOptions;
|
|
7
8
|
texts: ChatTexts;
|
|
9
|
+
contentRef?: RefObject<HTMLElement | null>;
|
|
10
|
+
side?: "start" | "end";
|
|
8
11
|
}): import("react").JSX.Element;
|
package/chat.css
CHANGED
|
@@ -79,9 +79,6 @@
|
|
|
79
79
|
.qsl\:z-10 {
|
|
80
80
|
z-index: 10;
|
|
81
81
|
}
|
|
82
|
-
.qsl\:z-\[1\] {
|
|
83
|
-
z-index: 1;
|
|
84
|
-
}
|
|
85
82
|
.qsl\:z-\[5\] {
|
|
86
83
|
z-index: 5;
|
|
87
84
|
}
|
|
@@ -170,6 +167,10 @@
|
|
|
170
167
|
width: calc(var(--qsl-spacing) * 4);
|
|
171
168
|
height: calc(var(--qsl-spacing) * 4);
|
|
172
169
|
}
|
|
170
|
+
.qsl\:size-5 {
|
|
171
|
+
width: calc(var(--qsl-spacing) * 5);
|
|
172
|
+
height: calc(var(--qsl-spacing) * 5);
|
|
173
|
+
}
|
|
173
174
|
.qsl\:size-6 {
|
|
174
175
|
width: calc(var(--qsl-spacing) * 6);
|
|
175
176
|
height: calc(var(--qsl-spacing) * 6);
|
|
@@ -186,6 +187,9 @@
|
|
|
186
187
|
width: calc(var(--qsl-spacing) * 9);
|
|
187
188
|
height: calc(var(--qsl-spacing) * 9);
|
|
188
189
|
}
|
|
190
|
+
.qsl\:h-5 {
|
|
191
|
+
height: calc(var(--qsl-spacing) * 5);
|
|
192
|
+
}
|
|
189
193
|
.qsl\:h-6 {
|
|
190
194
|
height: calc(var(--qsl-spacing) * 6);
|
|
191
195
|
}
|
|
@@ -395,6 +399,9 @@
|
|
|
395
399
|
.qsl\:gap-0 {
|
|
396
400
|
gap: 0px;
|
|
397
401
|
}
|
|
402
|
+
.qsl\:gap-0\.5 {
|
|
403
|
+
gap: calc(var(--qsl-spacing) * 0.5);
|
|
404
|
+
}
|
|
398
405
|
.qsl\:gap-1 {
|
|
399
406
|
gap: var(--qsl-spacing);
|
|
400
407
|
}
|
|
@@ -890,13 +897,6 @@
|
|
|
890
897
|
.qsl\:opacity-100 {
|
|
891
898
|
opacity: 100%;
|
|
892
899
|
}
|
|
893
|
-
.qsl\:shadow-\[0_2px_6px_color-mix\(in_srgb\,var\(--qsl-foreground\)_15\%\,transparent\)\] {
|
|
894
|
-
--tw-shadow: 0 2px 6px var(--tw-shadow-color, var(--qsl-foreground));
|
|
895
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
896
|
-
--tw-shadow: 0 2px 6px var(--tw-shadow-color, color-mix(in srgb,var(--qsl-foreground) 15%,transparent));
|
|
897
|
-
}
|
|
898
|
-
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
899
|
-
}
|
|
900
900
|
.qsl\:shadow-bar {
|
|
901
901
|
--tw-shadow: var(--qsl-bar-shadow);
|
|
902
902
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
@@ -951,6 +951,11 @@
|
|
|
951
951
|
transition-timing-function: var(--tw-ease, var(--qsl-default-transition-timing-function));
|
|
952
952
|
transition-duration: var(--tw-duration, var(--qsl-default-transition-duration));
|
|
953
953
|
}
|
|
954
|
+
.qsl\:transition-opacity {
|
|
955
|
+
transition-property: opacity;
|
|
956
|
+
transition-timing-function: var(--tw-ease, var(--qsl-default-transition-timing-function));
|
|
957
|
+
transition-duration: var(--tw-duration, var(--qsl-default-transition-duration));
|
|
958
|
+
}
|
|
954
959
|
.qsl\:transition-transform {
|
|
955
960
|
transition-property: transform, translate, scale, rotate;
|
|
956
961
|
transition-timing-function: var(--tw-ease, var(--qsl-default-transition-timing-function));
|
|
@@ -1000,10 +1005,13 @@
|
|
|
1000
1005
|
max-width: var(--qsl-chat-content-max-width,none);
|
|
1001
1006
|
}
|
|
1002
1007
|
@media (hover: hover) {
|
|
1003
|
-
.qsl\:group-hover\:pointer-events-auto:is(:where(.qsl\:group):hover *) {
|
|
1008
|
+
.qsl\:group-hover\/message\:pointer-events-auto:is(:where(.qsl\:group\/message):hover *) {
|
|
1004
1009
|
pointer-events: auto;
|
|
1005
1010
|
}
|
|
1006
|
-
.qsl\:group-hover\:opacity-
|
|
1011
|
+
.qsl\:group-hover\/message\:opacity-70:is(:where(.qsl\:group\/message):hover *) {
|
|
1012
|
+
opacity: 70%;
|
|
1013
|
+
}
|
|
1014
|
+
.qsl\:group-hover\/message\:opacity-100:is(:where(.qsl\:group\/message):hover *) {
|
|
1007
1015
|
opacity: 100%;
|
|
1008
1016
|
}
|
|
1009
1017
|
}
|
|
@@ -1031,6 +1039,9 @@
|
|
|
1031
1039
|
.qsl\:focus-within\:border-primary:focus-within {
|
|
1032
1040
|
border-color: var(--qsl-primary);
|
|
1033
1041
|
}
|
|
1042
|
+
.qsl\:focus-within\:opacity-70:focus-within {
|
|
1043
|
+
opacity: 70%;
|
|
1044
|
+
}
|
|
1034
1045
|
.qsl\:focus-within\:opacity-100:focus-within {
|
|
1035
1046
|
opacity: 100%;
|
|
1036
1047
|
}
|
|
@@ -1604,6 +1615,9 @@
|
|
|
1604
1615
|
.qsl\:\[\@media\(hover\:none\)\]\:pointer-events-auto {
|
|
1605
1616
|
pointer-events: auto;
|
|
1606
1617
|
}
|
|
1618
|
+
.qsl\:\[\@media\(hover\:none\)\]\:opacity-70 {
|
|
1619
|
+
opacity: 70%;
|
|
1620
|
+
}
|
|
1607
1621
|
.qsl\:\[\@media\(hover\:none\)\]\:opacity-100 {
|
|
1608
1622
|
opacity: 100%;
|
|
1609
1623
|
}
|
package/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { PendingActionCard } from "./PendingActionCard";
|
|
|
14
14
|
export { StepPopover } from "./StepPopover";
|
|
15
15
|
export { WorkingScenes, type WorkingScenesProps } from "./WorkingScenes";
|
|
16
16
|
export { announce } from "./announce";
|
|
17
|
-
export { useChatAction, copyChatText } from "./useChatAction";
|
|
17
|
+
export { useChatAction, copyChatText, copyChatHtml } from "./useChatAction";
|
|
18
18
|
export { createChatScroll } from "./chat-scroll";
|
|
19
19
|
export { ChatSendContext, createChatSendScope, type ChatSendScope } from "./ChatSendContext";
|
|
20
20
|
export { messageDate, timestampDay, timestampLabel } from "./timestamps";
|
package/index.js
CHANGED
|
@@ -57,7 +57,8 @@ var englishTexts = {
|
|
|
57
57
|
showLatestExchange: "Show latest reply between inputs",
|
|
58
58
|
showTimestamps: "Show timestamps",
|
|
59
59
|
hideTimestamps: "Hide timestamps",
|
|
60
|
-
copyMessage: "Copy
|
|
60
|
+
copyMessage: "Copy as Markdown",
|
|
61
|
+
copyFormatted: "Copy with formatting",
|
|
61
62
|
editMessage: "Edit message",
|
|
62
63
|
retryMessage: "Request answer again",
|
|
63
64
|
copyCode: "Copy code",
|
|
@@ -126,7 +127,8 @@ var germanTexts = {
|
|
|
126
127
|
showLatestExchange: "Letzte Antwort zwischen den Eingaben anzeigen",
|
|
127
128
|
showTimestamps: "Zeitstempel anzeigen",
|
|
128
129
|
hideTimestamps: "Zeitstempel ausblenden",
|
|
129
|
-
copyMessage: "
|
|
130
|
+
copyMessage: "Als Markdown kopieren",
|
|
131
|
+
copyFormatted: "Mit Formatierung kopieren",
|
|
130
132
|
editMessage: "Nachricht bearbeiten",
|
|
131
133
|
retryMessage: "Antwort erneut anfordern",
|
|
132
134
|
copyCode: "Code kopieren",
|
|
@@ -456,6 +458,15 @@ async function copyChatText(text, unavailable) {
|
|
|
456
458
|
if (!navigator.clipboard) throw new Error(unavailable);
|
|
457
459
|
await navigator.clipboard.writeText(text);
|
|
458
460
|
}
|
|
461
|
+
async function copyChatHtml(element, text, unavailable) {
|
|
462
|
+
if (!navigator.clipboard) throw new Error(unavailable);
|
|
463
|
+
if (typeof ClipboardItem === "undefined" || !navigator.clipboard.write) return navigator.clipboard.writeText(text);
|
|
464
|
+
const clone = element.cloneNode(true);
|
|
465
|
+
clone.querySelectorAll("button").forEach((button) => button.remove());
|
|
466
|
+
clone.querySelectorAll("[class]").forEach((node) => node.removeAttribute("class"));
|
|
467
|
+
const blob = (content, type) => new Blob([content], { type });
|
|
468
|
+
await navigator.clipboard.write([new ClipboardItem({ "text/html": blob(clone.innerHTML, "text/html"), "text/plain": blob(markdownPlainText(text), "text/plain") })]);
|
|
469
|
+
}
|
|
459
470
|
|
|
460
471
|
// packages/quassel/src/Markdown.tsx
|
|
461
472
|
import { jsx as jsx8, jsxs } from "react/jsx-runtime";
|
|
@@ -1041,37 +1052,46 @@ function useElapsed(start) {
|
|
|
1041
1052
|
}
|
|
1042
1053
|
|
|
1043
1054
|
// packages/quassel/src/MessageActions.tsx
|
|
1044
|
-
import { CheckIcon as CheckIcon3, CopyIcon as CopyIcon2, PencilIcon, RotateCcwIcon } from "lucide-react";
|
|
1055
|
+
import { CheckIcon as CheckIcon3, ClipboardTypeIcon, CopyIcon as CopyIcon2, PencilIcon, RotateCcwIcon } from "lucide-react";
|
|
1045
1056
|
import { useState as useState3 } from "react";
|
|
1046
1057
|
import { Fragment, jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1047
1058
|
var actionClasses = cn(
|
|
1048
|
-
"qsl:
|
|
1049
|
-
"qsl:
|
|
1050
|
-
"qsl:
|
|
1051
|
-
"qsl:[@media(hover:none)]:pointer-events-auto qsl:[@media(hover:none)]:opacity-100"
|
|
1059
|
+
"qsl:mt-0.5 qsl:flex qsl:h-5 qsl:items-center qsl:gap-0.5 qsl:text-muted-foreground qsl:opacity-0 qsl:transition-opacity",
|
|
1060
|
+
"qsl:pointer-events-none qsl:group-hover/message:pointer-events-auto qsl:focus-within:pointer-events-auto",
|
|
1061
|
+
"qsl:[@media(hover:none)]:pointer-events-auto"
|
|
1052
1062
|
);
|
|
1053
|
-
|
|
1063
|
+
var visibleClasses = "qsl:group-hover/message:opacity-100 qsl:focus-within:opacity-100 qsl:[@media(hover:none)]:opacity-100";
|
|
1064
|
+
var lightClasses = "qsl:group-hover/message:opacity-70 qsl:focus-within:opacity-70 qsl:[@media(hover:none)]:opacity-70";
|
|
1065
|
+
function MessageActions({ message, options, texts, contentRef, side = message.role === "user" ? "end" : "start" }) {
|
|
1054
1066
|
const action = useChatAction();
|
|
1055
1067
|
const { Button: Button2 } = useQuasselComponents();
|
|
1056
1068
|
const [lastAction, setLastAction] = useState3();
|
|
1057
|
-
const
|
|
1069
|
+
const enabled = (option, fallback) => typeof option === "function" ? option(message) : option ?? fallback;
|
|
1070
|
+
const copy = enabled(options?.copy, message.role === "user" || message.role === "assistant") && !!message.text.trim();
|
|
1071
|
+
const copyFormatted = copy && !!contentRef && enabled(options?.copyFormatted, message.role === "assistant");
|
|
1058
1072
|
const actions = [
|
|
1059
|
-
...copy
|
|
1073
|
+
...copy ? [{ id: "copy", label: texts.copyMessage, icon: /* @__PURE__ */ jsx12(CopyIcon2, {}), onClick: () => copyChatText(message.text, texts.clipboardUnavailable) }] : [],
|
|
1074
|
+
...copyFormatted ? [{
|
|
1075
|
+
id: "copy-formatted",
|
|
1076
|
+
label: texts.copyFormatted ?? defaultTexts.copyFormatted,
|
|
1077
|
+
icon: /* @__PURE__ */ jsx12(ClipboardTypeIcon, {}),
|
|
1078
|
+
onClick: () => contentRef.current ? copyChatHtml(contentRef.current, message.text, texts.clipboardUnavailable) : copyChatText(message.text, texts.clipboardUnavailable)
|
|
1079
|
+
}] : [],
|
|
1060
1080
|
...options?.edit && message.role === "user" ? [{ id: "edit", label: texts.editMessage, icon: /* @__PURE__ */ jsx12(PencilIcon, {}), onClick: options.edit }] : [],
|
|
1061
1081
|
...options?.retry && message.role === "assistant" ? [{ id: "retry", label: texts.retryMessage, icon: /* @__PURE__ */ jsx12(RotateCcwIcon, {}), onClick: options.retry }] : [],
|
|
1062
1082
|
...options?.custom?.(message) ?? []
|
|
1063
1083
|
];
|
|
1064
1084
|
return /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1065
|
-
actions.length > 0 && /* @__PURE__ */ jsx12("div", { className: actionClasses, children: actions.map((item) => {
|
|
1066
|
-
const copied =
|
|
1085
|
+
actions.length > 0 && /* @__PURE__ */ jsx12("div", { className: cn(actionClasses, message.role === "user" ? visibleClasses : lightClasses, side === "end" && "qsl:justify-end"), "data-chat": "message-actions", children: actions.map((item) => {
|
|
1086
|
+
const copied = ["copy", "copy-formatted"].includes(item.id) && lastAction === item.id && action.status === "done";
|
|
1067
1087
|
const label = copied ? texts.copied : item.label;
|
|
1068
1088
|
return /* @__PURE__ */ jsx12(
|
|
1069
1089
|
Button2,
|
|
1070
1090
|
{
|
|
1071
1091
|
"aria-label": label,
|
|
1072
|
-
className: "qsl:text-inherit",
|
|
1092
|
+
className: cn("qsl:text-inherit", item.icon && "qsl:size-5"),
|
|
1073
1093
|
disabled: item.disabled || action.status === "pending",
|
|
1074
|
-
size: item.icon ? "icon-
|
|
1094
|
+
size: item.icon ? "icon-xs" : "xs",
|
|
1075
1095
|
title: label,
|
|
1076
1096
|
variant: "ghost",
|
|
1077
1097
|
onClick: () => {
|
|
@@ -1731,6 +1751,7 @@ function Bubble({
|
|
|
1731
1751
|
messageActions
|
|
1732
1752
|
}) {
|
|
1733
1753
|
const stepSpacing = dense ? denseStepClasses : stepClasses;
|
|
1754
|
+
const textRef = useRef3(null);
|
|
1734
1755
|
if (message.role === "thinking" || message.role === "tool" && message.tool) {
|
|
1735
1756
|
if (detailMode === "compact") {
|
|
1736
1757
|
return /* @__PURE__ */ jsx13(TraceLine, { className: stepSpacing, elapsedAfter, expandable, message, texts, toolArgumentsText });
|
|
@@ -1777,38 +1798,43 @@ function Bubble({
|
|
|
1777
1798
|
"qsl:mb-[5px] qsl:table qsl:text-label qsl:leading-[1.45] qsl:font-semibold",
|
|
1778
1799
|
useBubble && message.bubble && "qsl:rounded-full qsl:border qsl:border-white/30 qsl:bg-black/15 qsl:px-[7px] qsl:py-px qsl:tracking-[0.02em]"
|
|
1779
1800
|
), "data-chat": "sender", children: sender }),
|
|
1780
|
-
/* @__PURE__ */ jsx13(Markdown, { text: message.text, streaming: message.role === "assistant" && !message.closed }),
|
|
1801
|
+
/* @__PURE__ */ jsx13("div", { "data-chat": "message-text", ref: textRef, children: /* @__PURE__ */ jsx13(Markdown, { text: message.text, streaming: message.role === "assistant" && !message.closed }) }),
|
|
1781
1802
|
/* @__PURE__ */ jsx13(MessageAttachments, { message, texts }),
|
|
1782
|
-
message.steered && /* @__PURE__ */ jsx13("small", { className: "qsl:mt-1 qsl:block qsl:text-label qsl:leading-[1.45] qsl:opacity-70", "data-chat": "steered", children: texts.steered })
|
|
1783
|
-
/* @__PURE__ */ jsx13(MessageActions, { message, options: messageActions, texts })
|
|
1803
|
+
message.steered && /* @__PURE__ */ jsx13("small", { className: "qsl:mt-1 qsl:block qsl:text-label qsl:leading-[1.45] qsl:opacity-70", "data-chat": "steered", children: texts.steered })
|
|
1784
1804
|
] });
|
|
1785
1805
|
const cursor = message.role === "assistant" && message.textCursor ? JSON.stringify(message.textCursor) : void 0;
|
|
1786
|
-
if (!useBubble) return /* @__PURE__ */
|
|
1806
|
+
if (!useBubble) return /* @__PURE__ */ jsxs6("div", { "data-chat-text-cursor": cursor, className: cn(stepSpacing, "qsl:group/message qsl:relative qsl:w-full qsl:leading-[var(--qsl-chat-line-height,1.65)] qsl:text-foreground"), "data-message": "answer", children: [
|
|
1807
|
+
content,
|
|
1808
|
+
/* @__PURE__ */ jsx13(MessageActions, { contentRef: textRef, message, options: messageActions, side, texts })
|
|
1809
|
+
] });
|
|
1787
1810
|
const fullBackground = message.role === "user" && bubbleOptions?.userBackgroundFullWidth !== false;
|
|
1788
1811
|
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";
|
|
1789
|
-
return /* @__PURE__ */ jsxs6(
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn(stepSpacing, "qsl:group/message"), children: [
|
|
1813
|
+
/* @__PURE__ */ jsxs6(
|
|
1814
|
+
"div",
|
|
1815
|
+
{
|
|
1816
|
+
"data-chat-text-cursor": cursor,
|
|
1817
|
+
className: cn("qsl:relative qsl:flex", side === "end" ? "qsl:justify-end" : "qsl:justify-start"),
|
|
1818
|
+
"data-message": message.role === "user" && !message.bubble && variant === "default" ? "user" : "bubble",
|
|
1819
|
+
"data-side": side,
|
|
1820
|
+
"data-background-width": fullBackground ? "full" : "content",
|
|
1821
|
+
children: [
|
|
1822
|
+
fullBackground && /* @__PURE__ */ jsx13("div", { "aria-hidden": "true", className: cn("qsl:pointer-events-none qsl:absolute qsl:inset-0", surfaceClasses), "data-chat": "bubble-background", style: { background: message.bubble?.color } }),
|
|
1823
|
+
/* @__PURE__ */ jsx13(
|
|
1824
|
+
"div",
|
|
1825
|
+
{
|
|
1826
|
+
className: cn(bubbleBodyClasses, !fullBackground && surfaceClasses, fullBackground && !message.bubble && "qsl:border qsl:border-transparent", message.bubble ? "qsl:text-white qsl:[--qsl-scroll-cover:var(--qsl-primary)]" : "qsl:text-foreground qsl:[--qsl-scroll-cover:var(--qsl-secondary)]"),
|
|
1827
|
+
"data-chat": "bubble-content",
|
|
1828
|
+
"data-tone": message.bubble ? "on-color" : void 0,
|
|
1829
|
+
style: { background: fullBackground ? void 0 : message.bubble?.color, maxWidth: bubbleOptions?.maxWidth },
|
|
1830
|
+
children: content
|
|
1831
|
+
}
|
|
1832
|
+
)
|
|
1833
|
+
]
|
|
1834
|
+
}
|
|
1835
|
+
),
|
|
1836
|
+
/* @__PURE__ */ jsx13(MessageActions, { contentRef: textRef, message, options: messageActions, side, texts })
|
|
1837
|
+
] });
|
|
1812
1838
|
}
|
|
1813
1839
|
function ActionContent({
|
|
1814
1840
|
action,
|
|
@@ -2602,6 +2628,7 @@ export {
|
|
|
2602
2628
|
attachmentDownloadUrl,
|
|
2603
2629
|
attachmentMediaType,
|
|
2604
2630
|
compactToolLine,
|
|
2631
|
+
copyChatHtml,
|
|
2605
2632
|
copyChatText,
|
|
2606
2633
|
createChatScroll,
|
|
2607
2634
|
createChatSendScope,
|