quassel 0.3.0 → 0.4.1
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/ChatActionDock.d.ts +1 -1
- package/ChatInputToolbar.d.ts +9 -9
- package/ChatMessages.d.ts +13 -11
- package/ChatPanel.d.ts +3 -3
- package/DetailModeSwitch.d.ts +4 -6
- package/Markdown.d.ts +1 -1
- package/PendingActionCard.d.ts +2 -2
- package/README.md +2 -2
- package/TimestampSwitch.d.ts +1 -2
- package/WorkingScenes.d.ts +4 -4
- package/announce.d.ts +1 -1
- package/{chunk-IGCFQJZB.js → chunk-3VEL4HEO.js} +9 -9
- package/chunk-3VEL4HEO.js.map +7 -0
- package/events.d.ts +8 -8
- package/events.js +1 -1
- package/index.js +443 -442
- package/index.js.map +3 -3
- package/package.json +1 -1
- package/texts.d.ts +0 -1
- package/types.d.ts +4 -4
- package/ui/stop-button.d.ts +2 -2
- package/chunk-IGCFQJZB.js.map +0 -7
package/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
chatAttachmentMediaType,
|
|
6
6
|
compactToolLine,
|
|
7
7
|
prettyJson
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3VEL4HEO.js";
|
|
9
9
|
|
|
10
10
|
// packages/quassel/src/types.ts
|
|
11
11
|
function stepState(message) {
|
|
@@ -21,68 +21,67 @@ function stepState(message) {
|
|
|
21
21
|
// packages/quassel/src/texts.ts
|
|
22
22
|
var fillText = (text, values) => text.replace(/\{(\w+)\}/g, (match, name) => name in values ? String(values[name]) : match);
|
|
23
23
|
var defaultTexts = {
|
|
24
|
-
working: "
|
|
25
|
-
toolRunning: "
|
|
26
|
-
toolStillRunning: "
|
|
24
|
+
working: "Working ...",
|
|
25
|
+
toolRunning: "running ...",
|
|
26
|
+
toolStillRunning: "Tool running ...",
|
|
27
27
|
toolElapsedSeconds: "{seconds} s",
|
|
28
|
-
currentTool: "
|
|
29
|
-
thinkingChip: "
|
|
30
|
-
toolChip: "
|
|
31
|
-
stepGroupOne: "1
|
|
32
|
-
stepGroupMany: "{count}
|
|
33
|
-
stepGroupCollapse: "
|
|
34
|
-
thinkingTitle: "Thinking
|
|
35
|
-
toolTitle: "Tool
|
|
36
|
-
argumentsLabel: "
|
|
37
|
-
resultLabel: "
|
|
38
|
-
close: "
|
|
39
|
-
jumpToEnd: "
|
|
40
|
-
send: "
|
|
41
|
-
sendIntoRun: "
|
|
42
|
-
stop: "
|
|
43
|
-
placeholder: "
|
|
44
|
-
steeringPlaceholder: "
|
|
45
|
-
steered: "
|
|
46
|
-
detailModeTitle: "
|
|
47
|
-
detailModeOff: "
|
|
48
|
-
detailModeCurrent: "
|
|
49
|
-
detailModeIcons: "
|
|
50
|
-
detailModeChips: "
|
|
51
|
-
detailModeGrouped: "
|
|
52
|
-
detailModeCompact: "
|
|
53
|
-
detailModeFull: "
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
attachmentKindVideo: "Video",
|
|
28
|
+
currentTool: "Tool running",
|
|
29
|
+
thinkingChip: "Thinking",
|
|
30
|
+
toolChip: "Tool",
|
|
31
|
+
stepGroupOne: "1 step",
|
|
32
|
+
stepGroupMany: "{count} steps",
|
|
33
|
+
stepGroupCollapse: "Collapse",
|
|
34
|
+
thinkingTitle: "Thinking trace",
|
|
35
|
+
toolTitle: "Tool call",
|
|
36
|
+
argumentsLabel: "Arguments",
|
|
37
|
+
resultLabel: "Result",
|
|
38
|
+
close: "Close",
|
|
39
|
+
jumpToEnd: "Jump to end",
|
|
40
|
+
send: "Send",
|
|
41
|
+
sendIntoRun: "Feed into the running turn",
|
|
42
|
+
stop: "Stop work",
|
|
43
|
+
placeholder: "Write a message ...",
|
|
44
|
+
steeringPlaceholder: "Interject ...",
|
|
45
|
+
steered: "Fed into the running turn",
|
|
46
|
+
detailModeTitle: "Step detail level",
|
|
47
|
+
detailModeOff: "answers only",
|
|
48
|
+
detailModeCurrent: "current",
|
|
49
|
+
detailModeIcons: "icons",
|
|
50
|
+
detailModeChips: "compact",
|
|
51
|
+
detailModeGrouped: "grouped",
|
|
52
|
+
detailModeCompact: "single line",
|
|
53
|
+
detailModeFull: "everything",
|
|
54
|
+
showTimestamps: "Show timestamps",
|
|
55
|
+
hideTimestamps: "Hide timestamps",
|
|
56
|
+
copyMessage: "Copy message",
|
|
57
|
+
editMessage: "Edit message",
|
|
58
|
+
retryMessage: "Request answer again",
|
|
59
|
+
copyCode: "Copy code",
|
|
60
|
+
copied: "Copied",
|
|
61
|
+
pendingAction: "waiting for input",
|
|
62
|
+
dismissAction: "Dismiss",
|
|
63
|
+
actionDismissed: "dismissed",
|
|
64
|
+
actionFailed: "Action failed",
|
|
65
|
+
clipboardUnavailable: "The clipboard is not available",
|
|
66
|
+
transcript: "Chat history",
|
|
67
|
+
attachments: "Attachments",
|
|
68
|
+
attachFiles: "Attach files",
|
|
69
|
+
attachFilesHint: "Attach files (up to {count} files, {size} in total)",
|
|
70
|
+
removeAttachment: "Remove {name}",
|
|
71
|
+
attachmentPreparing: "Preparing ...",
|
|
72
|
+
attachmentsPreparing: "Preparing attachments ...",
|
|
73
|
+
attachmentReadFailed: "The file could not be read. Remove it and add it again.",
|
|
74
|
+
attachmentsWhileBusy: "Wait until the input is ready again, then add the files again.",
|
|
75
|
+
tooManyAttachments: "At most {count} attachments per message. Remove an attachment first.",
|
|
76
|
+
attachmentsTooLarge: "Attachments may be at most {size} in total. Choose smaller files.",
|
|
77
|
+
attachmentUnsupported: "This chat does not support {name} as {kind}. Remove the attachment.",
|
|
78
|
+
attachmentUnsupportedByModel: "The model {model} does not support {name} as {kind}. Choose a suitable model or remove the attachment.",
|
|
79
|
+
attachmentKindImage: "image",
|
|
80
|
+
attachmentKindVideo: "video",
|
|
82
81
|
attachmentKindPdf: "PDF",
|
|
83
|
-
notSent: "
|
|
84
|
-
notSentAttachments: "({count}
|
|
85
|
-
insertUnsent: "
|
|
82
|
+
notSent: "Not sent:",
|
|
83
|
+
notSentAttachments: "({count} attachments)",
|
|
84
|
+
insertUnsent: "Insert unsent input"
|
|
86
85
|
};
|
|
87
86
|
|
|
88
87
|
// packages/quassel/src/ui/cn.ts
|
|
@@ -461,7 +460,7 @@ var components = {
|
|
|
461
460
|
a: ({ href, children }) => /* @__PURE__ */ jsx8(MarkdownLink, { href, children }),
|
|
462
461
|
pre: ({ children }) => /* @__PURE__ */ jsx8(CodeBlock, { children }),
|
|
463
462
|
code: ({ children, className }) => /* @__PURE__ */ jsx8("code", { className: cn(className, "qsl:rounded-sm qsl:bg-secondary qsl:px-1 qsl:py-0.5 qsl:font-mono qsl:text-sm qsl:[overflow-wrap:anywhere] qsl:in-data-[tone=on-color]:bg-white/20 qsl:in-data-[tone=on-color]:text-inherit", "qsl:[pre>&]:bg-transparent qsl:[pre>&]:p-0"), children }),
|
|
464
|
-
//
|
|
463
|
+
// Without max-content the table shrinks to the container and stacks letters.
|
|
465
464
|
table: ({ children }) => /* @__PURE__ */ jsx8("div", { className: cn(scrollbar, scrollCover, "qsl:my-2 qsl:overflow-x-auto"), children: /* @__PURE__ */ jsx8("table", { className: "qsl:w-max qsl:min-w-full qsl:border-collapse qsl:text-sm", children }) }),
|
|
466
465
|
thead: ({ children }) => /* @__PURE__ */ jsx8("thead", { children }),
|
|
467
466
|
tbody: ({ children }) => /* @__PURE__ */ jsx8("tbody", { children }),
|
|
@@ -495,7 +494,7 @@ function Markdown({ text, streaming = false }) {
|
|
|
495
494
|
);
|
|
496
495
|
}
|
|
497
496
|
function markdownPlainText(text) {
|
|
498
|
-
return text.split("\n").filter((line) => !/^\s*```\w*\s*$/.test(line) && !isTableDivider(line)).map((line) => (line.trim().startsWith("|") ? splitTableRow(line).join(", ") : line).replace(/^(#{1,6})\s+/, "").replace(/^\s*>\s?/, "").replace(/^\s*([-*+]|\d+[.)])\s+/, "").replace(/!?\[([^\]]*)\]\(([^)]+)\)|\*\*([^*]+)\*\*|__([^_]+)__|\*([^*]+)\*|`([^`]+)`/g, (_, link, _href,
|
|
497
|
+
return text.split("\n").filter((line) => !/^\s*```\w*\s*$/.test(line) && !isTableDivider(line)).map((line) => (line.trim().startsWith("|") ? splitTableRow(line).join(", ") : line).replace(/^(#{1,6})\s+/, "").replace(/^\s*>\s?/, "").replace(/^\s*([-*+]|\d+[.)])\s+/, "").replace(/!?\[([^\]]*)\]\(([^)]+)\)|\*\*([^*]+)\*\*|__([^_]+)__|\*([^*]+)\*|`([^`]+)`/g, (_, link, _href, bold, boldUnderscore, italic, code) => link ?? bold ?? boldUnderscore ?? italic ?? code).trim()).filter((line) => line !== "").join("\n");
|
|
499
498
|
}
|
|
500
499
|
function splitTableRow(line) {
|
|
501
500
|
let row = line.trim();
|
|
@@ -605,159 +604,159 @@ function PendingActionCard({
|
|
|
605
604
|
// packages/quassel/src/WorkingScenes.tsx
|
|
606
605
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
607
606
|
import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
608
|
-
var
|
|
609
|
-
var
|
|
610
|
-
var
|
|
611
|
-
function
|
|
612
|
-
return Array.from({ length:
|
|
607
|
+
var TRACK = 13;
|
|
608
|
+
var BASE_TICK_MS = 90;
|
|
609
|
+
var SCENE_CHANGE_MS = 2600;
|
|
610
|
+
function track(figures, width = TRACK) {
|
|
611
|
+
return Array.from({ length: width }, (_, x) => figures[x] ?? " ");
|
|
613
612
|
}
|
|
614
|
-
var
|
|
613
|
+
var SCENES = [
|
|
615
614
|
{
|
|
616
|
-
id: "
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const
|
|
620
|
-
if (
|
|
621
|
-
return "\u{1F914}" + "\u{1F4AD}".repeat(Math.min(
|
|
615
|
+
id: "thinking",
|
|
616
|
+
tick: 2,
|
|
617
|
+
draw: (step) => {
|
|
618
|
+
const tick = step % 10;
|
|
619
|
+
if (tick >= 8) return "\u{1F929}\u{1F4A1}";
|
|
620
|
+
return "\u{1F914}" + "\u{1F4AD}".repeat(Math.min(tick, 3));
|
|
622
621
|
}
|
|
623
622
|
},
|
|
624
623
|
{
|
|
625
|
-
//
|
|
626
|
-
id: "
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
const x =
|
|
631
|
-
return
|
|
624
|
+
// Flies to the right, dragging its tail behind.
|
|
625
|
+
id: "rocket",
|
|
626
|
+
tick: 2,
|
|
627
|
+
moving: true,
|
|
628
|
+
draw: (step) => {
|
|
629
|
+
const x = step % TRACK;
|
|
630
|
+
return track({ [x - 2]: "\xB7", [x - 1]: "\u{1F4A8}", [x]: "\u{1F680}" });
|
|
632
631
|
}
|
|
633
632
|
},
|
|
634
633
|
{
|
|
635
|
-
//
|
|
634
|
+
// The dots stay put and are eaten one by one.
|
|
636
635
|
id: "pacman",
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const x =
|
|
641
|
-
const
|
|
642
|
-
for (let i = x + 1; i <
|
|
643
|
-
|
|
636
|
+
tick: 2,
|
|
637
|
+
moving: true,
|
|
638
|
+
draw: (step) => {
|
|
639
|
+
const x = step % TRACK;
|
|
640
|
+
const figures = {};
|
|
641
|
+
for (let i = x + 1; i < TRACK; i++) {
|
|
642
|
+
figures[i] = "\xB7";
|
|
644
643
|
}
|
|
645
|
-
|
|
646
|
-
return
|
|
644
|
+
figures[x] = step % 2 === 0 ? "\u{1F7E1}" : "\u{1F315}";
|
|
645
|
+
return track(figures);
|
|
647
646
|
}
|
|
648
647
|
},
|
|
649
648
|
{
|
|
650
|
-
//
|
|
651
|
-
id: "
|
|
649
|
+
// A seed becomes a tree, then start over.
|
|
650
|
+
id: "growing",
|
|
652
651
|
compact: true,
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
return
|
|
652
|
+
tick: 2,
|
|
653
|
+
draw: (step) => {
|
|
654
|
+
const stages = ["\u{1F331}", "\u{1F331}", "\u{1F33F}", "\u{1F33F}", "\u{1F343}", "\u{1F333}", "\u{1F333}", "\u{1F333}"];
|
|
655
|
+
return stages[step % stages.length];
|
|
657
656
|
}
|
|
658
657
|
},
|
|
659
658
|
{
|
|
660
|
-
//
|
|
661
|
-
id: "
|
|
659
|
+
// The egg cracks, the chick hatches.
|
|
660
|
+
id: "hatching",
|
|
662
661
|
compact: true,
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const
|
|
666
|
-
if (
|
|
667
|
-
if (
|
|
668
|
-
if (
|
|
662
|
+
tick: 2,
|
|
663
|
+
draw: (step) => {
|
|
664
|
+
const tick = step % 12;
|
|
665
|
+
if (tick < 4) return "\u{1F95A}";
|
|
666
|
+
if (tick < 6) return "\u{1F95A}*";
|
|
667
|
+
if (tick < 9) return "\u{1F423}";
|
|
669
668
|
return "\u{1F425}";
|
|
670
669
|
}
|
|
671
670
|
},
|
|
672
671
|
{
|
|
673
|
-
// Morse-
|
|
672
|
+
// Morse-like signal that resolves into an OK.
|
|
674
673
|
id: "signal",
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
if (
|
|
679
|
-
const
|
|
680
|
-
return "\u{1F4E1} " +
|
|
674
|
+
tick: 2,
|
|
675
|
+
draw: (step) => {
|
|
676
|
+
const tick = step % 14;
|
|
677
|
+
if (tick >= 11) return "\u{1F4E1} OK";
|
|
678
|
+
const pattern = ["\xB7", "\xB7\xB7", "\xB7-", "-\xB7", "\xB7\xB7-", "-\xB7\xB7"];
|
|
679
|
+
return "\u{1F4E1} " + pattern[tick % pattern.length];
|
|
681
680
|
}
|
|
682
681
|
},
|
|
683
682
|
{
|
|
684
|
-
//
|
|
685
|
-
id: "
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
const
|
|
690
|
-
return (
|
|
683
|
+
// Hourglass: the sand runs through, then it is flipped.
|
|
684
|
+
id: "hourglass",
|
|
685
|
+
tick: 2,
|
|
686
|
+
draw: (step) => {
|
|
687
|
+
const tick = step % 12;
|
|
688
|
+
const grains = "\xB7".repeat(Math.max(0, 5 - Math.floor(tick / 2)));
|
|
689
|
+
return (tick < 10 ? "\u23F3" : "\u231B") + " " + grains;
|
|
691
690
|
}
|
|
692
691
|
},
|
|
693
692
|
{
|
|
694
|
-
//
|
|
695
|
-
id: "
|
|
693
|
+
// Egg, chick, hen - and she lays the next egg.
|
|
694
|
+
id: "chicken-cycle",
|
|
696
695
|
compact: true,
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
const
|
|
700
|
-
if (
|
|
701
|
-
if (
|
|
702
|
-
if (
|
|
703
|
-
if (
|
|
704
|
-
if (
|
|
696
|
+
tick: 3,
|
|
697
|
+
draw: (step) => {
|
|
698
|
+
const tick = step % 16;
|
|
699
|
+
if (tick < 3) return "\u{1F95A}";
|
|
700
|
+
if (tick < 5) return "\u{1F95A}*";
|
|
701
|
+
if (tick < 8) return "\u{1F423}";
|
|
702
|
+
if (tick < 11) return "\u{1F424}";
|
|
703
|
+
if (tick < 14) return "\u{1F414}";
|
|
705
704
|
return "\u{1F414}\u{1F95A}";
|
|
706
705
|
}
|
|
707
706
|
},
|
|
708
707
|
{
|
|
709
|
-
//
|
|
710
|
-
id: "
|
|
708
|
+
// Hat, sparks - and the rabbit bolts.
|
|
709
|
+
id: "magic-hat",
|
|
711
710
|
compact: true,
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
const
|
|
715
|
-
if (
|
|
716
|
-
if (
|
|
717
|
-
if (
|
|
711
|
+
tick: 3,
|
|
712
|
+
draw: (step) => {
|
|
713
|
+
const tick = step % 14;
|
|
714
|
+
if (tick < 4) return "\u{1F3A9}";
|
|
715
|
+
if (tick < 7) return "\u{1F3A9}*";
|
|
716
|
+
if (tick < 11) return "\u{1F3A9}\u{1F407}";
|
|
718
717
|
return "\u{1F407}\u{1F4A8}";
|
|
719
718
|
}
|
|
720
719
|
},
|
|
721
720
|
{
|
|
722
|
-
//
|
|
723
|
-
id: "
|
|
721
|
+
// Red, yellow, green - and off.
|
|
722
|
+
id: "traffic-light",
|
|
724
723
|
compact: true,
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const
|
|
728
|
-
if (
|
|
729
|
-
if (
|
|
730
|
-
if (
|
|
724
|
+
tick: 3,
|
|
725
|
+
draw: (step) => {
|
|
726
|
+
const tick = step % 12;
|
|
727
|
+
if (tick < 4) return "\u{1F534}";
|
|
728
|
+
if (tick < 6) return "\u{1F7E1}";
|
|
729
|
+
if (tick < 8) return "\u{1F7E2}";
|
|
731
730
|
return "\u{1F697}\u{1F4A8}";
|
|
732
731
|
}
|
|
733
732
|
},
|
|
734
733
|
{
|
|
735
|
-
//
|
|
736
|
-
id: "
|
|
734
|
+
// Ringing, oversleeping, running off.
|
|
735
|
+
id: "alarm-clock",
|
|
737
736
|
compact: true,
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const
|
|
741
|
-
if (
|
|
742
|
-
if (
|
|
743
|
-
if (
|
|
737
|
+
tick: 2,
|
|
738
|
+
draw: (step) => {
|
|
739
|
+
const tick = step % 14;
|
|
740
|
+
if (tick < 5) return tick % 2 === 0 ? "\u23F0" : "\u{1F514}";
|
|
741
|
+
if (tick < 8) return "\u{1F634}";
|
|
742
|
+
if (tick < 10) return "\u{1F633}";
|
|
744
743
|
return "\u{1F3C3}\u{1F4A8}";
|
|
745
744
|
}
|
|
746
745
|
},
|
|
747
746
|
{
|
|
748
|
-
//
|
|
749
|
-
id: "
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
const
|
|
753
|
-
if (
|
|
754
|
-
return
|
|
747
|
+
// A snail crawls to the flag - and celebrates.
|
|
748
|
+
id: "snail",
|
|
749
|
+
tick: 4,
|
|
750
|
+
draw: (step) => {
|
|
751
|
+
const tick = step % 8;
|
|
752
|
+
if (tick < 5) return track({ [tick]: "\u{1F40C}", 4: "\u{1F3C1}" }, 5);
|
|
753
|
+
return track({ 4: "\u{1F389}" }, 5);
|
|
755
754
|
}
|
|
756
755
|
}
|
|
757
756
|
];
|
|
758
|
-
var COMPACT_SCENES =
|
|
759
|
-
function WorkingScenes({ label = "
|
|
760
|
-
const scenes = compact ? COMPACT_SCENES :
|
|
757
|
+
var COMPACT_SCENES = SCENES.filter((scene) => scene.compact);
|
|
758
|
+
function WorkingScenes({ label = "Working ...", compact = false }) {
|
|
759
|
+
const scenes = compact ? COMPACT_SCENES : SCENES;
|
|
761
760
|
const [sceneIndex, setSceneIndex] = useState2(() => Math.floor(Math.random() * scenes.length));
|
|
762
761
|
const [offset, setOffset] = useState2(() => Math.floor(Math.random() * 20));
|
|
763
762
|
const [frameTick, setFrameTick] = useState2(0);
|
|
@@ -776,15 +775,15 @@ function WorkingScenes({ label = "Arbeitet ...", compact = false }) {
|
|
|
776
775
|
setSceneIndex((previous) => (previous + 1 + Math.floor(Math.random() * (scenes.length - 1))) % scenes.length);
|
|
777
776
|
setOffset(Math.floor(Math.random() * 20));
|
|
778
777
|
setFrameTick(0);
|
|
779
|
-
},
|
|
778
|
+
}, SCENE_CHANGE_MS);
|
|
780
779
|
return () => window.clearInterval(timer);
|
|
781
780
|
}, [reducedMotion, scenes]);
|
|
782
781
|
useEffect2(() => {
|
|
783
782
|
if (reducedMotion) return;
|
|
784
|
-
const timer = window.setInterval(() => setFrameTick((value) => value + 1),
|
|
783
|
+
const timer = window.setInterval(() => setFrameTick((value) => value + 1), BASE_TICK_MS);
|
|
785
784
|
return () => window.clearInterval(timer);
|
|
786
785
|
}, [reducedMotion]);
|
|
787
|
-
const frame = scene.
|
|
786
|
+
const frame = scene.draw(Math.floor(frameTick / scene.tick) + (scene.moving ? offset : 0));
|
|
788
787
|
return /* @__PURE__ */ jsxs4("span", { "aria-label": label, className: cn("qsl:flex qsl:items-center qsl:gap-2.5 qsl:text-[13px]", compact ? "qsl:text-inherit" : "qsl:mt-4 qsl:text-foreground"), "data-chat": "working", role: "status", children: [
|
|
789
788
|
!compact && /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", className: "qsl:text-muted-foreground qsl:opacity-70", children: ">" }),
|
|
790
789
|
/* @__PURE__ */ jsx11("span", { "aria-hidden": "true", className: cn("qsl:inline-block qsl:flex-none qsl:font-mono qsl:[font-variant-ligatures:none]", compact ? "qsl:w-[2.4em] qsl:text-center" : "qsl:w-[15rem]"), children: /* @__PURE__ */ jsx11("span", { className: "qsl:tracking-[0.02em] qsl:whitespace-pre qsl:motion-reduce:opacity-60", children: typeof frame === "string" ? frame : frame.map((cell, index) => /* @__PURE__ */ jsx11("span", { className: "qsl:inline-block qsl:w-[1.35em] qsl:text-center", children: cell }, index)) }) }, scene.id)
|
|
@@ -900,7 +899,7 @@ function formatElapsed(ms, texts) {
|
|
|
900
899
|
const seconds = String(total % 60).padStart(2, "0");
|
|
901
900
|
return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${seconds}` : `${minutes}:${seconds}`;
|
|
902
901
|
}
|
|
903
|
-
function createTicker(
|
|
902
|
+
function createTicker(intervalMs2 = tickMs) {
|
|
904
903
|
const listeners = /* @__PURE__ */ new Set();
|
|
905
904
|
let now = Date.now();
|
|
906
905
|
let timer;
|
|
@@ -912,7 +911,7 @@ function createTicker(intervalMs = tickMs) {
|
|
|
912
911
|
timer = setInterval(() => {
|
|
913
912
|
now = Date.now();
|
|
914
913
|
listeners.forEach((notify) => notify());
|
|
915
|
-
},
|
|
914
|
+
}, intervalMs2);
|
|
916
915
|
}
|
|
917
916
|
return () => {
|
|
918
917
|
listeners.delete(listener);
|
|
@@ -924,7 +923,7 @@ function createTicker(intervalMs = tickMs) {
|
|
|
924
923
|
},
|
|
925
924
|
now() {
|
|
926
925
|
const current = Date.now();
|
|
927
|
-
if (timer === void 0 && (current - now >=
|
|
926
|
+
if (timer === void 0 && (current - now >= intervalMs2 || current < now)) now = current;
|
|
928
927
|
return now;
|
|
929
928
|
},
|
|
930
929
|
active: () => timer !== void 0
|
|
@@ -1006,12 +1005,12 @@ import { createContext as createContext4 } from "react";
|
|
|
1006
1005
|
var ChatActionDock = createContext4(void 0);
|
|
1007
1006
|
|
|
1008
1007
|
// packages/quassel/src/announce.ts
|
|
1009
|
-
var
|
|
1010
|
-
var
|
|
1008
|
+
var intervalMs = 400;
|
|
1009
|
+
var maxQueued = 5;
|
|
1011
1010
|
var region;
|
|
1012
|
-
var
|
|
1013
|
-
var
|
|
1014
|
-
function
|
|
1011
|
+
var queued = [];
|
|
1012
|
+
var running = false;
|
|
1013
|
+
function liveRegion() {
|
|
1015
1014
|
if (region?.isConnected) {
|
|
1016
1015
|
return region;
|
|
1017
1016
|
}
|
|
@@ -1034,33 +1033,33 @@ function ansageRegion() {
|
|
|
1034
1033
|
region = element;
|
|
1035
1034
|
return element;
|
|
1036
1035
|
}
|
|
1037
|
-
function
|
|
1038
|
-
if (
|
|
1039
|
-
|
|
1036
|
+
function next() {
|
|
1037
|
+
if (queued.length === 0) {
|
|
1038
|
+
running = false;
|
|
1040
1039
|
return;
|
|
1041
1040
|
}
|
|
1042
1041
|
if (document.visibilityState === "hidden") {
|
|
1043
|
-
document.addEventListener("visibilitychange",
|
|
1042
|
+
document.addEventListener("visibilitychange", next, { once: true });
|
|
1044
1043
|
return;
|
|
1045
1044
|
}
|
|
1046
|
-
const [text, ...rest] =
|
|
1047
|
-
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
window.setTimeout(
|
|
1045
|
+
const [text, ...rest] = queued;
|
|
1046
|
+
queued = rest;
|
|
1047
|
+
const target = liveRegion();
|
|
1048
|
+
target.textContent = target.textContent === text ? `${text}\xA0` : text;
|
|
1049
|
+
window.setTimeout(next, intervalMs);
|
|
1051
1050
|
}
|
|
1052
1051
|
function announce(text) {
|
|
1053
|
-
const
|
|
1054
|
-
if (!
|
|
1052
|
+
const content = text.trim();
|
|
1053
|
+
if (!content || typeof document === "undefined") {
|
|
1055
1054
|
return;
|
|
1056
1055
|
}
|
|
1057
|
-
|
|
1058
|
-
if (
|
|
1056
|
+
queued = [...queued, content].slice(-maxQueued);
|
|
1057
|
+
if (running) {
|
|
1059
1058
|
return;
|
|
1060
1059
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
window.setTimeout(
|
|
1060
|
+
running = true;
|
|
1061
|
+
liveRegion();
|
|
1062
|
+
window.setTimeout(next, intervalMs);
|
|
1064
1063
|
}
|
|
1065
1064
|
|
|
1066
1065
|
// packages/quassel/src/ChatMessages.tsx
|
|
@@ -1083,12 +1082,12 @@ var chipClasses = cn(
|
|
|
1083
1082
|
);
|
|
1084
1083
|
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";
|
|
1085
1084
|
var collapseAtBottomFrom = 11;
|
|
1086
|
-
var
|
|
1085
|
+
var maxAnnouncementsAtOnce = 2;
|
|
1087
1086
|
var bubbleBodyClasses = "qsl:relative qsl:max-w-[86%] qsl:px-3 qsl:py-2 qsl:leading-[var(--qsl-chat-line-height,1.625)] qsl:[overflow-wrap:anywhere] qsl:@max-[620px]/chat:max-w-full";
|
|
1088
|
-
function
|
|
1087
|
+
function isStep(message) {
|
|
1089
1088
|
return !!message && (message.role === "thinking" || message.role === "tool");
|
|
1090
1089
|
}
|
|
1091
|
-
function
|
|
1090
|
+
function isOpenAction(message) {
|
|
1092
1091
|
return message.role === "action" && message.action !== void 0 && message.action.status === void 0;
|
|
1093
1092
|
}
|
|
1094
1093
|
function defaultArgumentsText(tool) {
|
|
@@ -1098,9 +1097,10 @@ function ChatMessages({
|
|
|
1098
1097
|
messages,
|
|
1099
1098
|
owner,
|
|
1100
1099
|
detailMode = "current",
|
|
1101
|
-
running = false,
|
|
1100
|
+
running: running2 = false,
|
|
1102
1101
|
working,
|
|
1103
1102
|
stepsExpandable = true,
|
|
1103
|
+
groupsExpandable = true,
|
|
1104
1104
|
texts,
|
|
1105
1105
|
toolArgumentsText = defaultArgumentsText,
|
|
1106
1106
|
renderTool,
|
|
@@ -1123,9 +1123,9 @@ function ChatMessages({
|
|
|
1123
1123
|
messageActions,
|
|
1124
1124
|
toolElapsedThreshold = 3e3
|
|
1125
1125
|
}) {
|
|
1126
|
-
const
|
|
1126
|
+
const allTexts = { ...defaultTexts, ...texts };
|
|
1127
1127
|
const { Button: Button2 } = useQuasselComponents();
|
|
1128
|
-
const
|
|
1128
|
+
const scrollArea = useRef3(null);
|
|
1129
1129
|
const threadRef = useRef3(null);
|
|
1130
1130
|
const scrollbarDragging = useRef3(false);
|
|
1131
1131
|
const touchY = useRef3(void 0);
|
|
@@ -1140,23 +1140,23 @@ function ChatMessages({
|
|
|
1140
1140
|
const timer = window.setInterval(() => setNow(Date.now()), 15e3);
|
|
1141
1141
|
return () => window.clearInterval(timer);
|
|
1142
1142
|
}, [showTimestamps, timestampOptions?.format]);
|
|
1143
|
-
const
|
|
1143
|
+
const announced = useRef3(new Set(messages.map((message) => message.key)));
|
|
1144
1144
|
useEffect3(() => {
|
|
1145
|
-
const
|
|
1146
|
-
if (
|
|
1145
|
+
const due = messages.filter((message, index) => !announced.current.has(message.key) && (message.role === "action" ? isOpenAction(message) : message.role === "assistant" && message.text.trim() !== "" && (message.closed === true || !running2 && index === messages.length - 1)));
|
|
1146
|
+
if (due.length === 0) {
|
|
1147
1147
|
return;
|
|
1148
1148
|
}
|
|
1149
|
-
|
|
1150
|
-
if (announce2 === false ||
|
|
1149
|
+
announced.current = /* @__PURE__ */ new Set([...announced.current, ...due.map((message) => message.key)]);
|
|
1150
|
+
if (announce2 === false || due.length > maxAnnouncementsAtOnce) {
|
|
1151
1151
|
return;
|
|
1152
1152
|
}
|
|
1153
|
-
|
|
1154
|
-
}, [messages,
|
|
1153
|
+
due.map((message) => announce2 ? announce2({ kind: message.role === "action" ? "action" : "reply", message }) : markdownPlainText(message.text)).forEach((text) => text && announce(text));
|
|
1154
|
+
}, [messages, running2, announce2]);
|
|
1155
1155
|
const updateJumpVisibility = useCallback2((viewport) => {
|
|
1156
1156
|
if (viewport.clientHeight > 0) setShowJumpToEnd(viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop > jumpToEndThreshold);
|
|
1157
1157
|
}, [jumpToEndThreshold]);
|
|
1158
1158
|
useLayoutEffect(() => sendScope?.subscribe(() => {
|
|
1159
|
-
const viewport =
|
|
1159
|
+
const viewport = scrollArea.current;
|
|
1160
1160
|
if (!viewport) return;
|
|
1161
1161
|
scroll.jump(viewport);
|
|
1162
1162
|
updateJumpVisibility(viewport);
|
|
@@ -1169,16 +1169,16 @@ function ChatMessages({
|
|
|
1169
1169
|
}
|
|
1170
1170
|
scroll.pause(viewport);
|
|
1171
1171
|
};
|
|
1172
|
-
const
|
|
1173
|
-
const
|
|
1174
|
-
if (
|
|
1175
|
-
scroll.scroll(
|
|
1176
|
-
updateJumpVisibility(
|
|
1172
|
+
const checkEnd = useCallback2(() => {
|
|
1173
|
+
const area = scrollArea.current;
|
|
1174
|
+
if (area) {
|
|
1175
|
+
scroll.scroll(area, scrollbarDragging.current);
|
|
1176
|
+
updateJumpVisibility(area);
|
|
1177
1177
|
}
|
|
1178
1178
|
}, [scroll, updateJumpVisibility]);
|
|
1179
1179
|
useEffect3(() => {
|
|
1180
1180
|
const finishDrag = () => {
|
|
1181
|
-
if (scrollbarDragging.current &&
|
|
1181
|
+
if (scrollbarDragging.current && scrollArea.current) scroll.scroll(scrollArea.current, true);
|
|
1182
1182
|
scrollbarDragging.current = false;
|
|
1183
1183
|
};
|
|
1184
1184
|
window.addEventListener("pointerup", finishDrag);
|
|
@@ -1189,27 +1189,27 @@ function ChatMessages({
|
|
|
1189
1189
|
};
|
|
1190
1190
|
}, [scroll]);
|
|
1191
1191
|
useLayoutEffect(() => {
|
|
1192
|
-
const
|
|
1193
|
-
if (!
|
|
1192
|
+
const area = scrollArea.current;
|
|
1193
|
+
if (!area) return;
|
|
1194
1194
|
const align = () => {
|
|
1195
|
-
scroll.layout(
|
|
1196
|
-
updateJumpVisibility(
|
|
1195
|
+
scroll.layout(area, scrollbarDragging.current);
|
|
1196
|
+
updateJumpVisibility(area);
|
|
1197
1197
|
};
|
|
1198
1198
|
align();
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
if (threadRef.current)
|
|
1202
|
-
return () =>
|
|
1199
|
+
const observer = new ResizeObserver(align);
|
|
1200
|
+
observer.observe(area);
|
|
1201
|
+
if (threadRef.current) observer.observe(threadRef.current, { box: "border-box" });
|
|
1202
|
+
return () => observer.disconnect();
|
|
1203
1203
|
});
|
|
1204
1204
|
const visible = useMemo3(
|
|
1205
1205
|
() => {
|
|
1206
|
-
const
|
|
1206
|
+
const lastNonUser = messages.reduce((last, message, index) => message.role === "user" ? last : index, -1);
|
|
1207
1207
|
return messages.filter((message, index) => {
|
|
1208
|
-
if (dock !== void 0 &&
|
|
1208
|
+
if (dock !== void 0 && isOpenAction(message)) {
|
|
1209
1209
|
return false;
|
|
1210
1210
|
}
|
|
1211
|
-
if (detailMode === "current" &&
|
|
1212
|
-
return
|
|
1211
|
+
if (detailMode === "current" && isStep(message)) {
|
|
1212
|
+
return running2 && index === lastNonUser && (message.role === "thinking" ? !message.closed : stepState(message) === "running");
|
|
1213
1213
|
}
|
|
1214
1214
|
if ((message.role === "thinking" || message.role === "assistant") && message.text.trim() === "" && !message.attachments?.length) {
|
|
1215
1215
|
return false;
|
|
@@ -1220,54 +1220,55 @@ function ChatMessages({
|
|
|
1220
1220
|
return true;
|
|
1221
1221
|
});
|
|
1222
1222
|
},
|
|
1223
|
-
[messages, detailMode,
|
|
1223
|
+
[messages, detailMode, running2, dock]
|
|
1224
1224
|
);
|
|
1225
|
-
const
|
|
1226
|
-
const
|
|
1225
|
+
const openActions = dock ? messages.filter(isOpenAction) : [];
|
|
1226
|
+
const withTime = (content, key, at, dense = false) => showTimestamps ? /* @__PURE__ */ jsxs6("div", { className: cn("qsl:flex qsl:items-baseline qsl:gap-2", dense ? denseStepClasses : stepClasses), children: [
|
|
1227
1227
|
/* @__PURE__ */ jsx13("time", { dateTime: messageDate(at)?.toISOString(), className: cn(
|
|
1228
1228
|
"qsl:flex-none qsl:text-meta qsl:tabular-nums qsl:text-muted-foreground qsl:opacity-85",
|
|
1229
1229
|
timestampOptions?.format && timestampOptions.format !== "time" ? "qsl:w-[clamp(60px,20%,120px)]" : "qsl:w-[34px]"
|
|
1230
1230
|
), children: messageDate(at) ? timestampLabel(messageDate(at), timestampOptions, now) : "" }),
|
|
1231
|
-
/* @__PURE__ */ jsx13("div", { className: "qsl:min-w-0 qsl:flex-1 qsl:[&>*:first-child]:mt-0", children:
|
|
1232
|
-
] }, `
|
|
1233
|
-
const
|
|
1234
|
-
const elapsedAfter =
|
|
1235
|
-
const
|
|
1236
|
-
const
|
|
1231
|
+
/* @__PURE__ */ jsx13("div", { className: "qsl:min-w-0 qsl:flex-1 qsl:[&>*:first-child]:mt-0", children: content })
|
|
1232
|
+
] }, `time-${key}`) : content;
|
|
1233
|
+
const chipMode = detailMode === "current" || detailMode === "chips" || detailMode === "icons";
|
|
1234
|
+
const elapsedAfter = running2 && toolElapsedThreshold !== false ? toolElapsedThreshold : void 0;
|
|
1235
|
+
const groupMode = detailMode === "grouped";
|
|
1236
|
+
const blocks = [];
|
|
1237
1237
|
let lastDay;
|
|
1238
|
-
let
|
|
1239
|
-
const
|
|
1240
|
-
if (
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1238
|
+
let group = [];
|
|
1239
|
+
const closeGroup = () => {
|
|
1240
|
+
if (group.length > 0) {
|
|
1241
|
+
const runningStep = running2 ? [...group].reverse().find((message) => ["running", "thinking"].includes(stepState(message))) : void 0;
|
|
1242
|
+
blocks.push(withTime(
|
|
1243
|
+
groupMode ? /* @__PURE__ */ jsx13(
|
|
1244
1244
|
StepGroup,
|
|
1245
1245
|
{
|
|
1246
1246
|
elapsedAfter,
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1247
|
+
expandable: stepsExpandable,
|
|
1248
|
+
groupExpandable: groupsExpandable,
|
|
1249
|
+
runningStep,
|
|
1250
|
+
messages: group,
|
|
1251
|
+
texts: allTexts,
|
|
1251
1252
|
toolArgumentsText
|
|
1252
1253
|
},
|
|
1253
|
-
|
|
1254
|
+
group[0].key
|
|
1254
1255
|
) : /* @__PURE__ */ jsx13(
|
|
1255
1256
|
StepRow,
|
|
1256
1257
|
{
|
|
1257
1258
|
elapsedAfter,
|
|
1258
|
-
|
|
1259
|
-
messages:
|
|
1260
|
-
|
|
1261
|
-
texts:
|
|
1259
|
+
expandable: stepsExpandable,
|
|
1260
|
+
messages: group,
|
|
1261
|
+
withText: detailMode !== "icons",
|
|
1262
|
+
texts: allTexts,
|
|
1262
1263
|
toolArgumentsText,
|
|
1263
|
-
toolLabel: detailMode === "current" && !stepsExpandable ?
|
|
1264
|
+
toolLabel: detailMode === "current" && !stepsExpandable ? allTexts.currentTool : void 0
|
|
1264
1265
|
},
|
|
1265
|
-
|
|
1266
|
+
group[0].key
|
|
1266
1267
|
),
|
|
1267
|
-
|
|
1268
|
-
|
|
1268
|
+
group[0].key,
|
|
1269
|
+
group[0].at
|
|
1269
1270
|
));
|
|
1270
|
-
|
|
1271
|
+
group = [];
|
|
1271
1272
|
}
|
|
1272
1273
|
};
|
|
1273
1274
|
visible.forEach((message, index) => {
|
|
@@ -1275,38 +1276,38 @@ function ChatMessages({
|
|
|
1275
1276
|
if (date) {
|
|
1276
1277
|
const day = timestampDay(date, timestampOptions);
|
|
1277
1278
|
if (day.key !== lastDay) {
|
|
1278
|
-
|
|
1279
|
-
|
|
1279
|
+
closeGroup();
|
|
1280
|
+
blocks.push(/* @__PURE__ */ jsx13("div", { className: cn(stepClasses, "qsl:text-center qsl:text-xs qsl:text-muted-foreground"), "data-chat": "day-separator", "data-day": day.key, children: day.label }, `day-${message.key}`));
|
|
1280
1281
|
lastDay = day.key;
|
|
1281
1282
|
}
|
|
1282
1283
|
}
|
|
1283
|
-
const
|
|
1284
|
-
if (
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
/* @__PURE__ */ jsx13("div", { className: stepClasses, children:
|
|
1284
|
+
const customRendering = message.role === "tool" && message.tool && detailMode !== "current" ? renderTool?.(message.tool) : void 0;
|
|
1285
|
+
if (customRendering !== void 0) {
|
|
1286
|
+
closeGroup();
|
|
1287
|
+
blocks.push(withTime(
|
|
1288
|
+
/* @__PURE__ */ jsx13("div", { className: stepClasses, children: customRendering }, message.key),
|
|
1288
1289
|
message.key,
|
|
1289
1290
|
message.at
|
|
1290
1291
|
));
|
|
1291
1292
|
return;
|
|
1292
1293
|
}
|
|
1293
|
-
if ((
|
|
1294
|
-
|
|
1294
|
+
if ((chipMode || groupMode) && isStep(message)) {
|
|
1295
|
+
group = [...group, message];
|
|
1295
1296
|
return;
|
|
1296
1297
|
}
|
|
1297
|
-
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1298
|
+
closeGroup();
|
|
1299
|
+
const dense = isStep(message) && isStep(visible[index - 1]);
|
|
1300
|
+
blocks.push(withTime(
|
|
1300
1301
|
/* @__PURE__ */ jsx13(
|
|
1301
1302
|
Bubble,
|
|
1302
1303
|
{
|
|
1303
1304
|
detailMode,
|
|
1304
|
-
|
|
1305
|
+
dense,
|
|
1305
1306
|
elapsedAfter,
|
|
1306
|
-
|
|
1307
|
+
expandable: stepsExpandable,
|
|
1307
1308
|
message,
|
|
1308
1309
|
plain: owner != null && message.sender === owner,
|
|
1309
|
-
texts:
|
|
1310
|
+
texts: allTexts,
|
|
1310
1311
|
toolArgumentsText,
|
|
1311
1312
|
renderAction,
|
|
1312
1313
|
onDismissAction,
|
|
@@ -1317,11 +1318,11 @@ function ChatMessages({
|
|
|
1317
1318
|
),
|
|
1318
1319
|
message.key,
|
|
1319
1320
|
message.at,
|
|
1320
|
-
|
|
1321
|
+
dense
|
|
1321
1322
|
));
|
|
1322
1323
|
});
|
|
1323
|
-
|
|
1324
|
-
const
|
|
1324
|
+
closeGroup();
|
|
1325
|
+
const transcript = /* @__PURE__ */ jsxs6("div", { className: cn("qsl:@container/chat qsl:relative qsl:flex qsl:min-h-0 qsl:flex-1 qsl:flex-col", className), "data-quassel": "", style: {
|
|
1325
1326
|
...appearanceStyle(appearance),
|
|
1326
1327
|
"--qsl-chat-content-max-width": typeof maxWidth === "number" ? `${maxWidth}px` : maxWidth,
|
|
1327
1328
|
"--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
|
|
@@ -1329,7 +1330,7 @@ function ChatMessages({
|
|
|
1329
1330
|
/* @__PURE__ */ jsx13(
|
|
1330
1331
|
"div",
|
|
1331
1332
|
{
|
|
1332
|
-
"aria-label":
|
|
1333
|
+
"aria-label": allTexts.transcript,
|
|
1333
1334
|
className: cn(
|
|
1334
1335
|
"qsl:min-h-0 qsl:flex-1 qsl:cursor-default qsl:overflow-y-auto qsl:text-[length:var(--qsl-chat-font-size,13px)] qsl:text-foreground",
|
|
1335
1336
|
"qsl:[--qsl-fade-edge:calc(100%_-_var(--qsl-composer-height,0px))]",
|
|
@@ -1339,7 +1340,7 @@ function ChatMessages({
|
|
|
1339
1340
|
scrollerClassName
|
|
1340
1341
|
),
|
|
1341
1342
|
"data-quassel-transcript": "",
|
|
1342
|
-
onScroll:
|
|
1343
|
+
onScroll: checkEnd,
|
|
1343
1344
|
onWheel: (event) => {
|
|
1344
1345
|
if (!event.ctrlKey && event.deltaY < 0) pauseUp(event.currentTarget, event.target);
|
|
1345
1346
|
},
|
|
@@ -1355,20 +1356,20 @@ function ChatMessages({
|
|
|
1355
1356
|
touchY.current = event.touches[0]?.clientY;
|
|
1356
1357
|
},
|
|
1357
1358
|
onTouchMove: (event) => {
|
|
1358
|
-
const
|
|
1359
|
-
if (
|
|
1360
|
-
touchY.current =
|
|
1359
|
+
const next2 = event.touches[0]?.clientY;
|
|
1360
|
+
if (next2 !== void 0 && touchY.current !== void 0 && next2 > touchY.current) pauseUp(event.currentTarget, event.target);
|
|
1361
|
+
touchY.current = next2;
|
|
1361
1362
|
},
|
|
1362
1363
|
role: "region",
|
|
1363
1364
|
tabIndex: 0,
|
|
1364
1365
|
ref: (element) => {
|
|
1365
|
-
|
|
1366
|
+
scrollArea.current = element;
|
|
1366
1367
|
scrollerRef?.(element);
|
|
1367
1368
|
},
|
|
1368
|
-
children: visible.length === 0 && !
|
|
1369
|
+
children: visible.length === 0 && !running2 ? emptyState ?? null : /* @__PURE__ */ jsxs6(
|
|
1369
1370
|
"div",
|
|
1370
1371
|
{
|
|
1371
|
-
"aria-busy":
|
|
1372
|
+
"aria-busy": running2 || void 0,
|
|
1372
1373
|
className: cn(
|
|
1373
1374
|
"qsl:mx-auto qsl:w-[calc(100%_-_2_*_var(--qsl-chat-horizontal-padding,24px))] qsl:max-w-[var(--qsl-chat-content-max-width,none)] qsl:pt-2",
|
|
1374
1375
|
"qsl:pb-[calc(var(--qsl-composer-height,0px)_+_max(3.25em,40px))]",
|
|
@@ -1376,52 +1377,52 @@ function ChatMessages({
|
|
|
1376
1377
|
),
|
|
1377
1378
|
ref: threadRef,
|
|
1378
1379
|
children: [
|
|
1379
|
-
|
|
1380
|
-
|
|
1380
|
+
blocks,
|
|
1381
|
+
running2 && (working ?? /* @__PURE__ */ jsx13(WorkingScenes, { label: allTexts.working }))
|
|
1381
1382
|
]
|
|
1382
1383
|
}
|
|
1383
1384
|
)
|
|
1384
1385
|
}
|
|
1385
1386
|
),
|
|
1386
|
-
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":
|
|
1387
|
-
if (
|
|
1388
|
-
scroll.jump(
|
|
1389
|
-
updateJumpVisibility(
|
|
1387
|
+
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: () => {
|
|
1388
|
+
if (scrollArea.current) {
|
|
1389
|
+
scroll.jump(scrollArea.current);
|
|
1390
|
+
updateJumpVisibility(scrollArea.current);
|
|
1390
1391
|
}
|
|
1391
1392
|
}, children: /* @__PURE__ */ jsx13(ChevronDownIcon, {}) }) }),
|
|
1392
|
-
dock &&
|
|
1393
|
-
/* @__PURE__ */ jsx13("div", { className: "qsl:flex qsl:flex-col qsl:gap-[var(--qsl-chat-dense-message-gap,8px)] qsl:text-[length:var(--qsl-chat-font-size,13px)] qsl:text-foreground", style: appearanceStyle(appearance), children:
|
|
1393
|
+
dock && openActions.length > 0 && createPortal(
|
|
1394
|
+
/* @__PURE__ */ jsx13("div", { className: "qsl:flex qsl:flex-col qsl:gap-[var(--qsl-chat-dense-message-gap,8px)] qsl:text-[length:var(--qsl-chat-font-size,13px)] qsl:text-foreground", style: appearanceStyle(appearance), children: openActions.map((message) => /* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts: allTexts }) }, message.key)) }),
|
|
1394
1395
|
dock
|
|
1395
1396
|
)
|
|
1396
1397
|
] });
|
|
1397
|
-
return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts:
|
|
1398
|
+
return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts: allTexts, children: transcript }) });
|
|
1398
1399
|
}
|
|
1399
1400
|
function StepRow({
|
|
1400
1401
|
messages,
|
|
1401
|
-
|
|
1402
|
-
|
|
1402
|
+
withText,
|
|
1403
|
+
expandable,
|
|
1403
1404
|
elapsedAfter,
|
|
1404
1405
|
texts,
|
|
1405
1406
|
toolArgumentsText,
|
|
1406
1407
|
toolLabel
|
|
1407
1408
|
}) {
|
|
1408
1409
|
const [detail, setDetail] = useState4();
|
|
1409
|
-
const
|
|
1410
|
+
const open = expandable && detail && messages.find((message) => message.key === detail.key);
|
|
1410
1411
|
return /* @__PURE__ */ jsxs6("div", { className: cn(stepClasses, "qsl:flex qsl:flex-wrap qsl:items-center qsl:gap-x-1.5 qsl:gap-y-1 qsl:pl-6"), "data-step": "row", children: [
|
|
1411
1412
|
messages.map((message) => {
|
|
1412
1413
|
const thinking = message.role === "thinking";
|
|
1413
1414
|
const tool = message.tool;
|
|
1414
1415
|
const state = stepState(message);
|
|
1415
|
-
const
|
|
1416
|
+
const running2 = state === "running" || state === "thinking";
|
|
1416
1417
|
const label = thinking ? texts.thinkingChip : toolLabel ?? (tool?.name || message.text || texts.toolChip);
|
|
1417
1418
|
const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1418
1419
|
thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { size: 11 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? "qsl:text-destructive" : void 0, size: 11 }),
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1420
|
+
withText && /* @__PURE__ */ jsx13("span", { className: "qsl:min-w-0 qsl:self-baseline qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap", children: label }),
|
|
1421
|
+
withText && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, className: "qsl:flex-none qsl:self-baseline", message, texts }),
|
|
1422
|
+
withText && /* @__PURE__ */ jsx13("span", { className: "qsl:inline-flex qsl:w-2.5 qsl:flex-none qsl:items-center qsl:justify-center qsl:text-success", children: state === "done" && /* @__PURE__ */ jsx13(CheckIcon4, { size: 10 }) })
|
|
1422
1423
|
] });
|
|
1423
|
-
const classes = cn(chipClasses, !
|
|
1424
|
-
if (!
|
|
1424
|
+
const classes = cn(chipClasses, !withText && "qsl:p-1", running2 && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none");
|
|
1425
|
+
if (!expandable) {
|
|
1425
1426
|
return /* @__PURE__ */ jsx13("span", { className: classes, "data-kind": thinking ? "thinking" : "tool", "data-state": state, "data-step": "chip", title: label, children: content }, message.key);
|
|
1426
1427
|
}
|
|
1427
1428
|
return /* @__PURE__ */ jsx13(
|
|
@@ -1440,10 +1441,10 @@ function StepRow({
|
|
|
1440
1441
|
message.key
|
|
1441
1442
|
);
|
|
1442
1443
|
}),
|
|
1443
|
-
|
|
1444
|
+
open && detail && /* @__PURE__ */ jsx13(
|
|
1444
1445
|
StepPopover,
|
|
1445
1446
|
{
|
|
1446
|
-
message:
|
|
1447
|
+
message: open,
|
|
1447
1448
|
position: detail.position,
|
|
1448
1449
|
texts,
|
|
1449
1450
|
toolArgumentsText,
|
|
@@ -1454,58 +1455,65 @@ function StepRow({
|
|
|
1454
1455
|
}
|
|
1455
1456
|
function StepGroup({
|
|
1456
1457
|
messages,
|
|
1457
|
-
|
|
1458
|
+
runningStep,
|
|
1458
1459
|
elapsedAfter,
|
|
1459
|
-
|
|
1460
|
+
expandable,
|
|
1461
|
+
groupExpandable,
|
|
1460
1462
|
texts,
|
|
1461
1463
|
toolArgumentsText
|
|
1462
1464
|
}) {
|
|
1463
|
-
const [
|
|
1464
|
-
const
|
|
1465
|
-
const
|
|
1465
|
+
const [open, setOpen] = useState4(false);
|
|
1466
|
+
const header = useRef3(null);
|
|
1467
|
+
const collapsedFromBottom = useRef3(false);
|
|
1466
1468
|
useLayoutEffect(() => {
|
|
1467
|
-
if (!
|
|
1468
|
-
|
|
1469
|
-
|
|
1469
|
+
if (!open && collapsedFromBottom.current) {
|
|
1470
|
+
collapsedFromBottom.current = false;
|
|
1471
|
+
header.current?.scrollIntoView({ block: "nearest" });
|
|
1470
1472
|
}
|
|
1471
|
-
}, [
|
|
1472
|
-
const
|
|
1473
|
-
const
|
|
1474
|
-
const
|
|
1475
|
-
const
|
|
1473
|
+
}, [open]);
|
|
1474
|
+
const active = runningStep !== void 0;
|
|
1475
|
+
const failed = messages.some((message) => message.tool?.isError);
|
|
1476
|
+
const count = messages.length === 1 ? texts.stepGroupOne : texts.stepGroupMany.replace("{count}", String(messages.length));
|
|
1477
|
+
const runningLabel = runningStep && (runningStep.role === "thinking" ? texts.thinkingChip : runningStep.tool?.name || runningStep.text || texts.toolChip);
|
|
1478
|
+
const summary = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1479
|
+
/* @__PURE__ */ jsx13(LayersIcon, { className: failed ? traceErrorIconClasses : traceIconClasses, size: 12 }),
|
|
1480
|
+
/* @__PURE__ */ jsxs6("span", { className: cn(traceLineClasses, "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap"), children: [
|
|
1481
|
+
count,
|
|
1482
|
+
runningLabel && /* @__PURE__ */ jsxs6("span", { className: traceRunningClasses, children: [
|
|
1483
|
+
runningLabel,
|
|
1484
|
+
" ",
|
|
1485
|
+
texts.toolRunning
|
|
1486
|
+
] }),
|
|
1487
|
+
runningStep && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message: runningStep, texts })
|
|
1488
|
+
] })
|
|
1489
|
+
] });
|
|
1490
|
+
if (!groupExpandable) {
|
|
1491
|
+
return /* @__PURE__ */ jsx13("div", { className: stepClasses, "data-step": "group", children: /* @__PURE__ */ jsx13("div", { className: cn(traceClasses, "qsl:items-center qsl:py-1 qsl:pr-2 qsl:pl-1", active && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none"), children: summary }) });
|
|
1492
|
+
}
|
|
1476
1493
|
return /* @__PURE__ */ jsxs6("div", { className: stepClasses, "data-step": "group", children: [
|
|
1477
1494
|
/* @__PURE__ */ jsxs6(
|
|
1478
1495
|
"button",
|
|
1479
1496
|
{
|
|
1480
|
-
"aria-expanded":
|
|
1481
|
-
className: cn(traceClasses, traceButtonClasses, "qsl:items-center qsl:pl-1",
|
|
1482
|
-
onClick: () =>
|
|
1483
|
-
ref:
|
|
1497
|
+
"aria-expanded": open,
|
|
1498
|
+
className: cn(traceClasses, traceButtonClasses, "qsl:items-center qsl:pl-1", active && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none"),
|
|
1499
|
+
onClick: () => setOpen((value) => !value),
|
|
1500
|
+
ref: header,
|
|
1484
1501
|
type: "button",
|
|
1485
1502
|
children: [
|
|
1486
|
-
/* @__PURE__ */ jsx13(ChevronRightIcon, { className: cn("qsl:flex-none qsl:opacity-60 qsl:transition-transform",
|
|
1487
|
-
|
|
1488
|
-
/* @__PURE__ */ jsxs6("span", { className: cn(traceLineClasses, "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap"), children: [
|
|
1489
|
-
anzahl,
|
|
1490
|
-
laufend && /* @__PURE__ */ jsxs6("span", { className: traceRunningClasses, children: [
|
|
1491
|
-
laufend,
|
|
1492
|
-
" ",
|
|
1493
|
-
texts.toolRunning
|
|
1494
|
-
] }),
|
|
1495
|
-
laufenderSchritt && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message: laufenderSchritt, texts })
|
|
1496
|
-
] })
|
|
1503
|
+
/* @__PURE__ */ jsx13(ChevronRightIcon, { className: cn("qsl:flex-none qsl:opacity-60 qsl:transition-transform", open && "qsl:rotate-90"), size: 12 }),
|
|
1504
|
+
summary
|
|
1497
1505
|
]
|
|
1498
1506
|
}
|
|
1499
1507
|
),
|
|
1500
|
-
|
|
1501
|
-
|
|
1508
|
+
open && /* @__PURE__ */ jsx13("div", { className: "qsl:mt-0.5 qsl:flex qsl:flex-col qsl:gap-0.5 qsl:pl-3", children: messages.map((message) => /* @__PURE__ */ jsx13(TraceLine, { className: "qsl:pl-3", elapsedAfter, expandable, message, texts, toolArgumentsText }, message.key)) }),
|
|
1509
|
+
open && messages.length >= collapseAtBottomFrom && /* @__PURE__ */ jsxs6(
|
|
1502
1510
|
"button",
|
|
1503
1511
|
{
|
|
1504
1512
|
className: cn(traceClasses, traceButtonClasses, "qsl:mt-0.5 qsl:items-center qsl:pl-1"),
|
|
1505
1513
|
"data-step": "group-collapse",
|
|
1506
1514
|
onClick: () => {
|
|
1507
|
-
|
|
1508
|
-
|
|
1515
|
+
collapsedFromBottom.current = true;
|
|
1516
|
+
setOpen(false);
|
|
1509
1517
|
},
|
|
1510
1518
|
type: "button",
|
|
1511
1519
|
children: [
|
|
@@ -1520,7 +1528,7 @@ function TraceLine({
|
|
|
1520
1528
|
message,
|
|
1521
1529
|
className,
|
|
1522
1530
|
elapsedAfter,
|
|
1523
|
-
|
|
1531
|
+
expandable,
|
|
1524
1532
|
texts,
|
|
1525
1533
|
toolArgumentsText
|
|
1526
1534
|
}) {
|
|
@@ -1528,18 +1536,18 @@ function TraceLine({
|
|
|
1528
1536
|
const thinking = message.role === "thinking";
|
|
1529
1537
|
const tool = message.tool;
|
|
1530
1538
|
const classes = cn(traceClasses, className);
|
|
1531
|
-
const
|
|
1532
|
-
const
|
|
1539
|
+
const line = cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap", "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap");
|
|
1540
|
+
const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1533
1541
|
thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { className: traceIconClasses, size: 12 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? traceErrorIconClasses : traceIconClasses, size: 12 }),
|
|
1534
|
-
/* @__PURE__ */ jsxs6("span", { className:
|
|
1542
|
+
/* @__PURE__ */ jsxs6("span", { className: line, children: [
|
|
1535
1543
|
message.text,
|
|
1536
1544
|
tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
|
|
1537
1545
|
/* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
|
|
1538
1546
|
] })
|
|
1539
1547
|
] });
|
|
1540
1548
|
const marks = { "data-compact": "true", "data-kind": thinking ? "thinking" : "tool", "data-step": "line" };
|
|
1541
|
-
if (!
|
|
1542
|
-
return /* @__PURE__ */ jsx13("div", { className: classes, ...marks, children:
|
|
1549
|
+
if (!expandable) {
|
|
1550
|
+
return /* @__PURE__ */ jsx13("div", { className: classes, ...marks, children: content });
|
|
1543
1551
|
}
|
|
1544
1552
|
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1545
1553
|
/* @__PURE__ */ jsx13(
|
|
@@ -1551,7 +1559,7 @@ function TraceLine({
|
|
|
1551
1559
|
...marks,
|
|
1552
1560
|
onClick: (event) => setDetailPosition({ x: event.clientX, y: event.clientY }),
|
|
1553
1561
|
type: "button",
|
|
1554
|
-
children:
|
|
1562
|
+
children: content
|
|
1555
1563
|
}
|
|
1556
1564
|
),
|
|
1557
1565
|
detailPosition && /* @__PURE__ */ jsx13(
|
|
@@ -1579,9 +1587,9 @@ function Bubble({
|
|
|
1579
1587
|
message,
|
|
1580
1588
|
plain,
|
|
1581
1589
|
detailMode,
|
|
1582
|
-
|
|
1590
|
+
dense,
|
|
1583
1591
|
elapsedAfter,
|
|
1584
|
-
|
|
1592
|
+
expandable,
|
|
1585
1593
|
texts,
|
|
1586
1594
|
toolArgumentsText,
|
|
1587
1595
|
renderAction,
|
|
@@ -1589,23 +1597,23 @@ function Bubble({
|
|
|
1589
1597
|
bubbleOptions,
|
|
1590
1598
|
messageActions
|
|
1591
1599
|
}) {
|
|
1592
|
-
const
|
|
1600
|
+
const stepSpacing = dense ? denseStepClasses : stepClasses;
|
|
1593
1601
|
if (message.role === "thinking" || message.role === "tool" && message.tool) {
|
|
1594
1602
|
if (detailMode === "compact") {
|
|
1595
|
-
return /* @__PURE__ */ jsx13(TraceLine, { className:
|
|
1603
|
+
return /* @__PURE__ */ jsx13(TraceLine, { className: stepSpacing, elapsedAfter, expandable, message, texts, toolArgumentsText });
|
|
1596
1604
|
}
|
|
1597
1605
|
const thinking = message.role === "thinking";
|
|
1598
1606
|
const tool = message.tool;
|
|
1599
1607
|
const icon = thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { className: traceIconClasses, size: 12 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? traceErrorIconClasses : traceIconClasses, size: 12 });
|
|
1600
|
-
const
|
|
1608
|
+
const line = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1601
1609
|
message.text,
|
|
1602
1610
|
tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
|
|
1603
1611
|
/* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
|
|
1604
1612
|
] });
|
|
1605
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses,
|
|
1613
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses, stepSpacing), "data-kind": thinking ? "thinking" : "tool", "data-step": "detail", children: [
|
|
1606
1614
|
icon,
|
|
1607
1615
|
/* @__PURE__ */ jsxs6("div", { className: "qsl:min-w-0 qsl:flex-1", children: [
|
|
1608
|
-
/* @__PURE__ */ jsx13("div", { className: cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap"), children:
|
|
1616
|
+
/* @__PURE__ */ jsx13("div", { className: cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap"), children: line }),
|
|
1609
1617
|
tool && /* @__PURE__ */ jsxs6("div", { className: "qsl:mt-0.5 qsl:border-l-2 qsl:border-border-soft qsl:pl-2", children: [
|
|
1610
1618
|
/* @__PURE__ */ jsx13("pre", { className: "qsl:mb-0.5 qsl:max-h-[208px] qsl:overflow-auto qsl:font-mono qsl:text-meta qsl:leading-[1.375] qsl:break-all qsl:whitespace-pre-wrap qsl:opacity-80", children: toolArgumentsText(tool) }),
|
|
1611
1619
|
tool.result !== void 0 && /* @__PURE__ */ jsx13("pre", { className: cn(
|
|
@@ -1617,10 +1625,10 @@ function Bubble({
|
|
|
1617
1625
|
] });
|
|
1618
1626
|
}
|
|
1619
1627
|
if (message.role === "action" && message.action) {
|
|
1620
|
-
return /* @__PURE__ */ jsx13("div", { className:
|
|
1628
|
+
return /* @__PURE__ */ jsx13("div", { className: stepSpacing, children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts }) });
|
|
1621
1629
|
}
|
|
1622
1630
|
if (message.role === "system") {
|
|
1623
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn(
|
|
1631
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn(stepSpacing, "qsl:border-l-2 qsl:border-destructive/50 qsl:pl-3 qsl:text-sm qsl:text-muted-foreground"), "data-message": "system", children: [
|
|
1624
1632
|
/* @__PURE__ */ jsx13(Markdown, { text: message.text }),
|
|
1625
1633
|
/* @__PURE__ */ jsx13(MessageAttachments, { label: texts.attachments, message })
|
|
1626
1634
|
] });
|
|
@@ -1642,12 +1650,12 @@ function Bubble({
|
|
|
1642
1650
|
/* @__PURE__ */ jsx13(MessageActions, { message, options: messageActions, texts })
|
|
1643
1651
|
] });
|
|
1644
1652
|
const cursor = message.role === "assistant" && message.textCursor ? JSON.stringify(message.textCursor) : void 0;
|
|
1645
|
-
if (!useBubble) return /* @__PURE__ */ jsx13("div", { "data-chat-text-cursor": cursor, className: cn(
|
|
1653
|
+
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.625)] qsl:text-foreground"), "data-message": "answer", children: content });
|
|
1646
1654
|
return /* @__PURE__ */ jsx13(
|
|
1647
1655
|
"div",
|
|
1648
1656
|
{
|
|
1649
1657
|
"data-chat-text-cursor": cursor,
|
|
1650
|
-
className: cn(
|
|
1658
|
+
className: cn(stepSpacing, "qsl:flex", side === "end" ? "qsl:justify-end" : "qsl:justify-start"),
|
|
1651
1659
|
"data-message": message.role === "user" && !message.bubble && variant === "default" ? "user" : "bubble",
|
|
1652
1660
|
"data-side": side,
|
|
1653
1661
|
children: /* @__PURE__ */ jsx13(
|
|
@@ -1706,8 +1714,8 @@ function ChatPanel({
|
|
|
1706
1714
|
appearance,
|
|
1707
1715
|
scrollOnSend = false
|
|
1708
1716
|
}) {
|
|
1709
|
-
const
|
|
1710
|
-
const
|
|
1717
|
+
const root = useRef4(null);
|
|
1718
|
+
const composerRef = useRef4(null);
|
|
1711
1719
|
const [dock, setDock] = useState5(null);
|
|
1712
1720
|
const hasComposer = composer !== void 0;
|
|
1713
1721
|
const scrollOnSendRef = useRef4(scrollOnSend);
|
|
@@ -1716,23 +1724,23 @@ function ChatPanel({
|
|
|
1716
1724
|
scrollOnSendRef.current = scrollOnSend;
|
|
1717
1725
|
}, [scrollOnSend]);
|
|
1718
1726
|
useEffect4(() => {
|
|
1719
|
-
const element =
|
|
1720
|
-
const
|
|
1721
|
-
if (!element || !
|
|
1727
|
+
const element = composerRef.current;
|
|
1728
|
+
const frame = root.current;
|
|
1729
|
+
if (!element || !frame) {
|
|
1722
1730
|
return;
|
|
1723
1731
|
}
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1726
|
-
|
|
1732
|
+
const measure = () => {
|
|
1733
|
+
frame.style.setProperty("--qsl-composer-height", `${element.offsetHeight}px`);
|
|
1734
|
+
frame.style.setProperty("--qsl-panel-height", `${frame.offsetHeight}px`);
|
|
1727
1735
|
};
|
|
1728
|
-
const
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1736
|
+
const observer = new ResizeObserver(measure);
|
|
1737
|
+
observer.observe(element);
|
|
1738
|
+
observer.observe(frame);
|
|
1739
|
+
measure();
|
|
1732
1740
|
return () => {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1741
|
+
observer.disconnect();
|
|
1742
|
+
frame.style.removeProperty("--qsl-composer-height");
|
|
1743
|
+
frame.style.removeProperty("--qsl-panel-height");
|
|
1736
1744
|
};
|
|
1737
1745
|
}, [hasComposer]);
|
|
1738
1746
|
return /* @__PURE__ */ jsx14(ChatSendContext.Provider, { value: sendScope, children: /* @__PURE__ */ jsx14(ChatActionDock.Provider, { value: hasComposer ? dock : void 0, children: /* @__PURE__ */ jsxs7(
|
|
@@ -1747,7 +1755,7 @@ function ChatPanel({
|
|
|
1747
1755
|
"--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
|
|
1748
1756
|
},
|
|
1749
1757
|
ref: (element) => {
|
|
1750
|
-
|
|
1758
|
+
root.current = element;
|
|
1751
1759
|
if (typeof nodeRef === "function") {
|
|
1752
1760
|
nodeRef(element);
|
|
1753
1761
|
} else if (nodeRef) {
|
|
@@ -1756,7 +1764,7 @@ function ChatPanel({
|
|
|
1756
1764
|
},
|
|
1757
1765
|
children: [
|
|
1758
1766
|
children,
|
|
1759
|
-
hasComposer && /* @__PURE__ */ jsxs7("div", { className: "qsl:absolute qsl:inset-x-0 qsl:bottom-0 qsl:z-[5] qsl:px-[var(--qsl-chat-horizontal-padding,24px)] qsl:pb-3.5 qsl:*:mx-auto qsl:*:max-w-[var(--qsl-chat-content-max-width,none)]", "data-chat": "composer", ref:
|
|
1767
|
+
hasComposer && /* @__PURE__ */ jsxs7("div", { className: "qsl:absolute qsl:inset-x-0 qsl:bottom-0 qsl:z-[5] qsl:px-[var(--qsl-chat-horizontal-padding,24px)] qsl:pb-3.5 qsl:*:mx-auto qsl:*:max-w-[var(--qsl-chat-content-max-width,none)]", "data-chat": "composer", ref: composerRef, children: [
|
|
1760
1768
|
/* @__PURE__ */ jsx14("div", { children: /* @__PURE__ */ jsx14(
|
|
1761
1769
|
"div",
|
|
1762
1770
|
{
|
|
@@ -1834,7 +1842,7 @@ var attachmentMediaClasses = "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondar
|
|
|
1834
1842
|
function ChatInputToolbar({
|
|
1835
1843
|
onSend,
|
|
1836
1844
|
onStop,
|
|
1837
|
-
running = false,
|
|
1845
|
+
running: running2 = false,
|
|
1838
1846
|
disabled = false,
|
|
1839
1847
|
sendDisabled = false,
|
|
1840
1848
|
layout = "card",
|
|
@@ -1859,7 +1867,7 @@ function ChatInputToolbar({
|
|
|
1859
1867
|
onAutoFocusSettled
|
|
1860
1868
|
}) {
|
|
1861
1869
|
const sendScope = useContext4(ChatSendContext);
|
|
1862
|
-
const
|
|
1870
|
+
const allTexts = { ...defaultTexts, ...texts };
|
|
1863
1871
|
const { Button: Button2, StopButton: StopButton2, Toggle: Toggle2 } = useQuasselComponents();
|
|
1864
1872
|
const [draft, setDraft] = useState7(initialValue);
|
|
1865
1873
|
const [sending, setSending] = useState7(false);
|
|
@@ -1883,11 +1891,11 @@ function ChatInputToolbar({
|
|
|
1883
1891
|
const addFiles = (files) => {
|
|
1884
1892
|
if (!files.length) return;
|
|
1885
1893
|
if (disabled || pendingSend.current) {
|
|
1886
|
-
setAttachmentError(
|
|
1894
|
+
setAttachmentError(allTexts.attachmentsWhileBusy);
|
|
1887
1895
|
return;
|
|
1888
1896
|
}
|
|
1889
1897
|
try {
|
|
1890
|
-
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files,
|
|
1898
|
+
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files, allTexts);
|
|
1891
1899
|
} catch (error) {
|
|
1892
1900
|
setAttachmentError(error instanceof Error ? error.message : String(error));
|
|
1893
1901
|
return;
|
|
@@ -1901,17 +1909,17 @@ function ChatInputToolbar({
|
|
|
1901
1909
|
() => updateAttachments((current) => current.map((item) => item.id === entry.id ? {
|
|
1902
1910
|
...entry,
|
|
1903
1911
|
status: "error",
|
|
1904
|
-
error:
|
|
1912
|
+
error: allTexts.attachmentReadFailed
|
|
1905
1913
|
} : item))
|
|
1906
1914
|
);
|
|
1907
1915
|
}
|
|
1908
1916
|
};
|
|
1909
1917
|
const rootRef = useRef6(null);
|
|
1910
|
-
const
|
|
1918
|
+
const textareaRef = useRef6(null);
|
|
1911
1919
|
const autoFocusSettled = useRef6(false);
|
|
1912
1920
|
useEffect5(() => {
|
|
1913
1921
|
if (!autoFocus || autoFocusSettled.current) return;
|
|
1914
|
-
const input2 =
|
|
1922
|
+
const input2 = textareaRef.current;
|
|
1915
1923
|
if (!input2) return;
|
|
1916
1924
|
let frame = 0;
|
|
1917
1925
|
const settle = () => {
|
|
@@ -1960,7 +1968,7 @@ function ChatInputToolbar({
|
|
|
1960
1968
|
disabled: disabled || sending,
|
|
1961
1969
|
onFiles: addFiles,
|
|
1962
1970
|
onPasteText: (text) => {
|
|
1963
|
-
const input2 =
|
|
1971
|
+
const input2 = textareaRef.current;
|
|
1964
1972
|
if (!input2 || disabled || pendingSend.current) return;
|
|
1965
1973
|
const start = input2.selectionStart;
|
|
1966
1974
|
const end = input2.selectionEnd;
|
|
@@ -1997,11 +2005,11 @@ function ChatInputToolbar({
|
|
|
1997
2005
|
insert: (text) => {
|
|
1998
2006
|
pendingFocus.current = true;
|
|
1999
2007
|
changeDraft(text);
|
|
2000
|
-
const element =
|
|
2008
|
+
const element = textareaRef.current;
|
|
2001
2009
|
element?.focus();
|
|
2002
2010
|
if (element?.value === text) element.setSelectionRange(text.length, text.length);
|
|
2003
2011
|
},
|
|
2004
|
-
focus: () =>
|
|
2012
|
+
focus: () => textareaRef.current?.focus(),
|
|
2005
2013
|
submit: (onSubmit, options) => send(onSubmit, options?.allowEmpty),
|
|
2006
2014
|
reset: () => {
|
|
2007
2015
|
generation.current += 1;
|
|
@@ -2024,31 +2032,31 @@ function ChatInputToolbar({
|
|
|
2024
2032
|
return;
|
|
2025
2033
|
}
|
|
2026
2034
|
pendingFocus.current = false;
|
|
2027
|
-
const element =
|
|
2035
|
+
const element = textareaRef.current;
|
|
2028
2036
|
if (element) {
|
|
2029
2037
|
element.focus();
|
|
2030
2038
|
element.setSelectionRange(element.value.length, element.value.length);
|
|
2031
2039
|
}
|
|
2032
2040
|
}, [draft, sending]);
|
|
2033
2041
|
const applyAutoGrow = useCallback3(() => {
|
|
2034
|
-
const element =
|
|
2042
|
+
const element = textareaRef.current;
|
|
2035
2043
|
if (!element || inputMaxRows === void 0) {
|
|
2036
2044
|
return;
|
|
2037
2045
|
}
|
|
2038
|
-
const
|
|
2039
|
-
const
|
|
2040
|
-
const
|
|
2041
|
-
const maximum =
|
|
2046
|
+
const style = getComputedStyle(element);
|
|
2047
|
+
const lineHeight = Number.parseFloat(style.lineHeight) || Number.parseFloat(style.fontSize) * 1.4;
|
|
2048
|
+
const padding = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom);
|
|
2049
|
+
const maximum = lineHeight * Math.max(inputMaxRows, inputRows) + padding;
|
|
2042
2050
|
element.style.height = "auto";
|
|
2043
|
-
const
|
|
2044
|
-
element.style.height = `${Math.min(
|
|
2045
|
-
element.style.overflowY =
|
|
2051
|
+
const contentHeight = element.scrollHeight;
|
|
2052
|
+
element.style.height = `${Math.min(contentHeight, maximum)}px`;
|
|
2053
|
+
element.style.overflowY = contentHeight > maximum ? "auto" : "hidden";
|
|
2046
2054
|
}, [inputMaxRows, inputRows]);
|
|
2047
2055
|
useEffect5(() => {
|
|
2048
2056
|
applyAutoGrow();
|
|
2049
2057
|
}, [draft, applyAutoGrow]);
|
|
2050
2058
|
useEffect5(() => {
|
|
2051
|
-
const element =
|
|
2059
|
+
const element = textareaRef.current;
|
|
2052
2060
|
if (!element || inputMaxRows === void 0) {
|
|
2053
2061
|
return;
|
|
2054
2062
|
}
|
|
@@ -2069,9 +2077,9 @@ function ChatInputToolbar({
|
|
|
2069
2077
|
}
|
|
2070
2078
|
const update = () => setCompact(element.clientWidth < COMPACT_WIDTH_PX);
|
|
2071
2079
|
update();
|
|
2072
|
-
const
|
|
2073
|
-
|
|
2074
|
-
return () =>
|
|
2080
|
+
const observer = new ResizeObserver(update);
|
|
2081
|
+
observer.observe(element);
|
|
2082
|
+
return () => observer.disconnect();
|
|
2075
2083
|
}, []);
|
|
2076
2084
|
const hasAttachments = attachments.length > 0;
|
|
2077
2085
|
useEffect5(() => {
|
|
@@ -2080,7 +2088,7 @@ function ChatInputToolbar({
|
|
|
2080
2088
|
const capabilityError = hasAttachments && attachmentCapabilitiesError ? attachmentCapabilitiesError : attachmentCapabilities ? attachmentCapabilityError(attachments.map((entry) => ({
|
|
2081
2089
|
name: entry.file.name,
|
|
2082
2090
|
mediaType: attachmentMediaType(entry.file)
|
|
2083
|
-
})), attachmentCapabilities,
|
|
2091
|
+
})), attachmentCapabilities, allTexts) : void 0;
|
|
2084
2092
|
const hasContent = draft.trim().length > 0 || attachments.length > 0;
|
|
2085
2093
|
const preparing = attachments.some((entry) => entry.status === "reading");
|
|
2086
2094
|
const attachmentBlocked = attachments.some((entry) => entry.status !== "ready");
|
|
@@ -2099,7 +2107,7 @@ function ChatInputToolbar({
|
|
|
2099
2107
|
const sentRevision = draftRevision.current;
|
|
2100
2108
|
const sentDraft = draft;
|
|
2101
2109
|
const focusOrigin = document.activeElement;
|
|
2102
|
-
const startedFocused = focusOrigin ===
|
|
2110
|
+
const startedFocused = focusOrigin === textareaRef.current || rootRef.current?.contains(focusOrigin);
|
|
2103
2111
|
let leftComposer = false;
|
|
2104
2112
|
const observeFocus = (event) => {
|
|
2105
2113
|
const target = event.target;
|
|
@@ -2130,7 +2138,7 @@ function ChatInputToolbar({
|
|
|
2130
2138
|
document.removeEventListener("pointerdown", observeFocus);
|
|
2131
2139
|
if (generation.current === sentGeneration) {
|
|
2132
2140
|
pendingSend.current = false;
|
|
2133
|
-
pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !==
|
|
2141
|
+
pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !== textareaRef.current) && (document.activeElement === focusOrigin || !toolbar && document.activeElement === document.body);
|
|
2134
2142
|
setSending(false);
|
|
2135
2143
|
}
|
|
2136
2144
|
}
|
|
@@ -2141,28 +2149,28 @@ function ChatInputToolbar({
|
|
|
2141
2149
|
if (!event.repeat) void send();
|
|
2142
2150
|
}
|
|
2143
2151
|
};
|
|
2144
|
-
const attachmentPreviews = attachments.length > 0 && /* @__PURE__ */ jsx15("ul", { className: "qsl:flex qsl:max-h-[240px] qsl:flex-wrap qsl:gap-2 qsl:overflow-y-auto qsl:px-3 qsl:pt-2.5 qsl:in-data-[tone=overseer-details]:max-h-40 qsl:in-data-[tone=overseer-details]:overflow-y-auto", "aria-label":
|
|
2152
|
+
const attachmentPreviews = attachments.length > 0 && /* @__PURE__ */ jsx15("ul", { className: "qsl:flex qsl:max-h-[240px] qsl:flex-wrap qsl:gap-2 qsl:overflow-y-auto qsl:px-3 qsl:pt-2.5 qsl:in-data-[tone=overseer-details]:max-h-40 qsl:in-data-[tone=overseer-details]:overflow-y-auto", "aria-label": allTexts.attachments, children: attachments.map((entry) => /* @__PURE__ */ jsxs8("li", { className: attachmentItemClasses, children: [
|
|
2145
2153
|
entry.status === "ready" && entry.input.mediaType.startsWith("image/") && /* @__PURE__ */ jsx15("img", { className: attachmentMediaClasses, src: `data:${entry.input.mediaType};base64,${entry.input.data}`, alt: entry.file.name }),
|
|
2146
2154
|
entry.status === "ready" && entry.input.mediaType.startsWith("video/") && /* @__PURE__ */ jsx15("video", { className: attachmentMediaClasses, controls: true, preload: "metadata", "aria-label": entry.file.name, src: `data:${entry.input.mediaType};base64,${entry.input.data}` }),
|
|
2147
2155
|
/* @__PURE__ */ jsxs8("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:pr-8 qsl:[overflow-wrap:anywhere]", children: [
|
|
2148
2156
|
/* @__PURE__ */ jsx15("span", { className: "qsl:font-medium", title: entry.file.name, children: entry.file.name }),
|
|
2149
2157
|
/* @__PURE__ */ jsx15("small", { children: formatAttachmentSize(entry.file.size) }),
|
|
2150
|
-
entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children:
|
|
2158
|
+
entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children: allTexts.attachmentPreparing }),
|
|
2151
2159
|
entry.status === "error" && /* @__PURE__ */ jsx15("small", { role: "alert", children: entry.error })
|
|
2152
2160
|
] }),
|
|
2153
2161
|
/* @__PURE__ */ jsx15(
|
|
2154
2162
|
Button2,
|
|
2155
2163
|
{
|
|
2156
|
-
"aria-label": fillText(
|
|
2164
|
+
"aria-label": fillText(allTexts.removeAttachment, { name: entry.file.name }),
|
|
2157
2165
|
className: "qsl:absolute qsl:top-1 qsl:right-1",
|
|
2158
2166
|
disabled: disabled || sending,
|
|
2159
2167
|
onClick: () => {
|
|
2160
2168
|
updateAttachments((current) => current.filter((item) => item.id !== entry.id));
|
|
2161
2169
|
setAttachmentError(void 0);
|
|
2162
|
-
|
|
2170
|
+
textareaRef.current?.focus();
|
|
2163
2171
|
},
|
|
2164
2172
|
size: "icon-sm",
|
|
2165
|
-
title: fillText(
|
|
2173
|
+
title: fillText(allTexts.removeAttachment, { name: entry.file.name }),
|
|
2166
2174
|
variant: "outline",
|
|
2167
2175
|
children: /* @__PURE__ */ jsx15(XIcon3, {})
|
|
2168
2176
|
}
|
|
@@ -2174,16 +2182,16 @@ function ChatInputToolbar({
|
|
|
2174
2182
|
sendError && /* @__PURE__ */ jsx15("p", { className: errorClasses, role: "alert", children: sendError }),
|
|
2175
2183
|
failedSubmissions.map((failed) => /* @__PURE__ */ jsxs8("div", { className: cn(errorClasses, "qsl:flex qsl:flex-wrap qsl:items-center qsl:gap-2"), role: "status", children: [
|
|
2176
2184
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2177
|
-
|
|
2185
|
+
allTexts.notSent,
|
|
2178
2186
|
" ",
|
|
2179
2187
|
failed.draft.slice(0, 100),
|
|
2180
2188
|
failed.draft.length > 100 ? " ..." : "",
|
|
2181
|
-
failed.attachments.length > 0 ? ` ${fillText(
|
|
2189
|
+
failed.attachments.length > 0 ? ` ${fillText(allTexts.notSentAttachments, { count: failed.attachments.length })}` : "",
|
|
2182
2190
|
" "
|
|
2183
2191
|
] }),
|
|
2184
2192
|
/* @__PURE__ */ jsx15(Button2, { disabled: disabled || sending, size: "sm", variant: "outline", onClick: () => {
|
|
2185
2193
|
try {
|
|
2186
|
-
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file),
|
|
2194
|
+
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file), allTexts);
|
|
2187
2195
|
} catch (error) {
|
|
2188
2196
|
setAttachmentError(error instanceof Error ? error.message : String(error));
|
|
2189
2197
|
return;
|
|
@@ -2194,22 +2202,22 @@ function ChatInputToolbar({
|
|
|
2194
2202
|
setFailedSubmissions((current) => current.filter((entry) => entry.id !== failed.id));
|
|
2195
2203
|
setAttachmentError(void 0);
|
|
2196
2204
|
pendingFocus.current = true;
|
|
2197
|
-
}, children:
|
|
2205
|
+
}, children: allTexts.insertUnsent })
|
|
2198
2206
|
] }, failed.id))
|
|
2199
2207
|
] });
|
|
2200
2208
|
const sendButtons = /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
2201
|
-
|
|
2202
|
-
(!
|
|
2209
|
+
running2 && onStop && !hasContent && /* @__PURE__ */ jsx15(StopButton2, { className: "qsl:flex-none", label: allTexts.stop, onClick: onStop, size: "icon-sm" }),
|
|
2210
|
+
(!running2 || !onStop || hasContent) && /* @__PURE__ */ jsx15(
|
|
2203
2211
|
Button2,
|
|
2204
2212
|
{
|
|
2205
|
-
"aria-label":
|
|
2213
|
+
"aria-label": allTexts.send,
|
|
2206
2214
|
className: "qsl:flex-none",
|
|
2207
2215
|
disabled: disabled || sendDisabled || sending || attachmentBlocked || !!capabilityError || !hasContent,
|
|
2208
2216
|
onClick: () => {
|
|
2209
2217
|
void send();
|
|
2210
2218
|
},
|
|
2211
2219
|
size: "icon-sm",
|
|
2212
|
-
title:
|
|
2220
|
+
title: running2 ? allTexts.sendIntoRun : allTexts.send,
|
|
2213
2221
|
children: /* @__PURE__ */ jsx15(SendIcon, { size: 14 })
|
|
2214
2222
|
}
|
|
2215
2223
|
)
|
|
@@ -2219,16 +2227,16 @@ function ChatInputToolbar({
|
|
|
2219
2227
|
attachmentsEnabled && /* @__PURE__ */ jsx15(
|
|
2220
2228
|
Button2,
|
|
2221
2229
|
{
|
|
2222
|
-
"aria-label":
|
|
2230
|
+
"aria-label": allTexts.attachFiles,
|
|
2223
2231
|
disabled: disabled || sending,
|
|
2224
2232
|
onClick: () => fileInput.inputRef.current?.click(),
|
|
2225
2233
|
size: "icon-sm",
|
|
2226
|
-
title: fillText(
|
|
2234
|
+
title: fillText(allTexts.attachFilesHint, { count: MAX_CHAT_ATTACHMENTS, size: maxAttachmentSizeLabel }),
|
|
2227
2235
|
variant: "ghost",
|
|
2228
2236
|
children: /* @__PURE__ */ jsx15(PaperclipIcon, {})
|
|
2229
2237
|
}
|
|
2230
2238
|
),
|
|
2231
|
-
preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children:
|
|
2239
|
+
preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children: allTexts.attachmentsPreparing }),
|
|
2232
2240
|
actions?.map((action, index) => action.active === void 0 ? /* @__PURE__ */ jsxs8(Button2, { disabled: action.disabled, onClick: action.onClick, size: "sm", title: action.title ?? action.label, variant: "ghost", children: [
|
|
2233
2241
|
action.icon,
|
|
2234
2242
|
action.label && /* @__PURE__ */ jsx15("span", { className: action.icon ? "qsl:in-data-[compact=true]:hidden" : void 0, children: action.label })
|
|
@@ -2253,13 +2261,13 @@ function ChatInputToolbar({
|
|
|
2253
2261
|
{
|
|
2254
2262
|
"aria-controls": inputAriaControls,
|
|
2255
2263
|
className: textareaClasses,
|
|
2256
|
-
"aria-label":
|
|
2264
|
+
"aria-label": allTexts.placeholder,
|
|
2257
2265
|
disabled: !toolbar && disabled,
|
|
2258
2266
|
readOnly: toolbar && disabled,
|
|
2259
2267
|
onChange: (event) => changeDraft(event.target.value),
|
|
2260
2268
|
onKeyDown,
|
|
2261
|
-
placeholder:
|
|
2262
|
-
ref:
|
|
2269
|
+
placeholder: running2 ? allTexts.steeringPlaceholder : allTexts.placeholder,
|
|
2270
|
+
ref: textareaRef,
|
|
2263
2271
|
rows: inputRows,
|
|
2264
2272
|
value: draft
|
|
2265
2273
|
}
|
|
@@ -2271,14 +2279,14 @@ function ChatInputToolbar({
|
|
|
2271
2279
|
"data-compact": compact,
|
|
2272
2280
|
"data-layout": layout,
|
|
2273
2281
|
"data-quassel": "",
|
|
2274
|
-
"data-working":
|
|
2282
|
+
"data-working": running2 || sending,
|
|
2275
2283
|
ref: rootRef,
|
|
2276
2284
|
...attachmentsEnabled ? fileInput.dropProps : {},
|
|
2277
2285
|
children: [
|
|
2278
2286
|
attachmentsEnabled && /* @__PURE__ */ jsx15(
|
|
2279
2287
|
"input",
|
|
2280
2288
|
{
|
|
2281
|
-
"aria-label":
|
|
2289
|
+
"aria-label": allTexts.attachFiles,
|
|
2282
2290
|
hidden: true,
|
|
2283
2291
|
multiple: true,
|
|
2284
2292
|
type: "file",
|
|
@@ -2306,8 +2314,8 @@ function ChatInputToolbar({
|
|
|
2306
2314
|
}
|
|
2307
2315
|
|
|
2308
2316
|
// packages/quassel/src/DetailModeSwitch.tsx
|
|
2309
|
-
import {
|
|
2310
|
-
import { jsx as jsx16
|
|
2317
|
+
import { CircleDotIcon, EyeOffIcon, ListCollapseIcon, ListIcon, ShapesIcon, TagsIcon, TextAlignJustifyIcon } from "lucide-react";
|
|
2318
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2311
2319
|
var DETAIL_MODES = ["off", "current", "icons", "chips", "grouped", "compact", "full"];
|
|
2312
2320
|
var TEXT_KEYS = {
|
|
2313
2321
|
off: "detailModeOff",
|
|
@@ -2318,57 +2326,50 @@ var TEXT_KEYS = {
|
|
|
2318
2326
|
compact: "detailModeCompact",
|
|
2319
2327
|
full: "detailModeFull"
|
|
2320
2328
|
};
|
|
2329
|
+
var ICONS = {
|
|
2330
|
+
off: EyeOffIcon,
|
|
2331
|
+
current: CircleDotIcon,
|
|
2332
|
+
icons: ShapesIcon,
|
|
2333
|
+
chips: TagsIcon,
|
|
2334
|
+
grouped: ListCollapseIcon,
|
|
2335
|
+
compact: ListIcon,
|
|
2336
|
+
full: TextAlignJustifyIcon
|
|
2337
|
+
};
|
|
2321
2338
|
var detailModeLabel = (mode, texts) => ({ ...defaultTexts, ...texts })[TEXT_KEYS[mode]];
|
|
2322
2339
|
function DetailModeSwitch({
|
|
2323
2340
|
mode,
|
|
2324
2341
|
onChange,
|
|
2325
2342
|
modes = DETAIL_MODES,
|
|
2326
|
-
collapsible = true,
|
|
2327
2343
|
texts,
|
|
2328
2344
|
className
|
|
2329
2345
|
}) {
|
|
2330
|
-
const
|
|
2346
|
+
const allTexts = { ...defaultTexts, ...texts };
|
|
2331
2347
|
const { Button: Button2 } = useQuasselComponents();
|
|
2332
|
-
const
|
|
2333
|
-
const
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
"aria-label": `${alleTexte.detailModeTitle}: ${detailModeLabel(mode, texts)}`,
|
|
2338
|
-
className,
|
|
2339
|
-
onClick: () => onChange(naechster),
|
|
2340
|
-
size: "sm",
|
|
2341
|
-
title: `${alleTexte.detailModeTitle}: ${detailModeLabel(mode, texts)}`,
|
|
2342
|
-
variant: "ghost",
|
|
2343
|
-
children: [
|
|
2344
|
-
/* @__PURE__ */ jsx16(SparklesIcon3, {}),
|
|
2345
|
-
/* @__PURE__ */ jsx16("span", { className: collapsible ? "qsl:in-data-[compact=true]:hidden" : void 0, children: detailModeLabel(mode, texts) })
|
|
2346
|
-
]
|
|
2347
|
-
}
|
|
2348
|
-
);
|
|
2348
|
+
const current = modes.indexOf(mode);
|
|
2349
|
+
const next2 = modes[(current < 0 ? 0 : current + 1) % modes.length];
|
|
2350
|
+
const Icon = ICONS[mode];
|
|
2351
|
+
const title = `${allTexts.detailModeTitle}: ${detailModeLabel(mode, texts)}`;
|
|
2352
|
+
return /* @__PURE__ */ jsx16(Button2, { "aria-label": title, className, onClick: () => onChange(next2), size: "icon-sm", title, variant: "ghost", children: /* @__PURE__ */ jsx16(Icon, {}) });
|
|
2349
2353
|
}
|
|
2350
2354
|
|
|
2351
2355
|
// packages/quassel/src/TimestampSwitch.tsx
|
|
2352
|
-
import { ClockIcon } from "lucide-react";
|
|
2353
|
-
import { jsx as jsx17
|
|
2354
|
-
function TimestampSwitch({ showTimestamps, onChange,
|
|
2356
|
+
import { ClockFadingIcon, ClockIcon } from "lucide-react";
|
|
2357
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2358
|
+
function TimestampSwitch({ showTimestamps, onChange, texts, className }) {
|
|
2355
2359
|
const labels = { ...defaultTexts, ...texts };
|
|
2356
2360
|
const { Button: Button2 } = useQuasselComponents();
|
|
2357
2361
|
const title = showTimestamps ? labels.hideTimestamps : labels.showTimestamps;
|
|
2358
|
-
return /* @__PURE__ */
|
|
2362
|
+
return /* @__PURE__ */ jsx17(
|
|
2359
2363
|
Button2,
|
|
2360
2364
|
{
|
|
2361
2365
|
"aria-label": title,
|
|
2362
2366
|
"aria-pressed": showTimestamps,
|
|
2363
2367
|
className,
|
|
2364
2368
|
onClick: () => onChange(!showTimestamps),
|
|
2365
|
-
size: "sm",
|
|
2369
|
+
size: "icon-sm",
|
|
2366
2370
|
title,
|
|
2367
2371
|
variant: "ghost",
|
|
2368
|
-
children:
|
|
2369
|
-
/* @__PURE__ */ jsx17(ClockIcon, {}),
|
|
2370
|
-
/* @__PURE__ */ jsx17("span", { className: collapsible ? "qsl:in-data-[compact=true]:hidden" : void 0, children: labels.timestamps })
|
|
2371
|
-
]
|
|
2372
|
+
children: showTimestamps ? /* @__PURE__ */ jsx17(ClockIcon, {}) : /* @__PURE__ */ jsx17(ClockFadingIcon, {})
|
|
2372
2373
|
}
|
|
2373
2374
|
);
|
|
2374
2375
|
}
|