quassel 0.3.0 → 0.4.0
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 +10 -10
- 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 +429 -437
- 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,7 +1097,7 @@ 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,
|
|
1104
1103
|
texts,
|
|
@@ -1123,9 +1122,9 @@ function ChatMessages({
|
|
|
1123
1122
|
messageActions,
|
|
1124
1123
|
toolElapsedThreshold = 3e3
|
|
1125
1124
|
}) {
|
|
1126
|
-
const
|
|
1125
|
+
const allTexts = { ...defaultTexts, ...texts };
|
|
1127
1126
|
const { Button: Button2 } = useQuasselComponents();
|
|
1128
|
-
const
|
|
1127
|
+
const scrollArea = useRef3(null);
|
|
1129
1128
|
const threadRef = useRef3(null);
|
|
1130
1129
|
const scrollbarDragging = useRef3(false);
|
|
1131
1130
|
const touchY = useRef3(void 0);
|
|
@@ -1140,23 +1139,23 @@ function ChatMessages({
|
|
|
1140
1139
|
const timer = window.setInterval(() => setNow(Date.now()), 15e3);
|
|
1141
1140
|
return () => window.clearInterval(timer);
|
|
1142
1141
|
}, [showTimestamps, timestampOptions?.format]);
|
|
1143
|
-
const
|
|
1142
|
+
const announced = useRef3(new Set(messages.map((message) => message.key)));
|
|
1144
1143
|
useEffect3(() => {
|
|
1145
|
-
const
|
|
1146
|
-
if (
|
|
1144
|
+
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)));
|
|
1145
|
+
if (due.length === 0) {
|
|
1147
1146
|
return;
|
|
1148
1147
|
}
|
|
1149
|
-
|
|
1150
|
-
if (announce2 === false ||
|
|
1148
|
+
announced.current = /* @__PURE__ */ new Set([...announced.current, ...due.map((message) => message.key)]);
|
|
1149
|
+
if (announce2 === false || due.length > maxAnnouncementsAtOnce) {
|
|
1151
1150
|
return;
|
|
1152
1151
|
}
|
|
1153
|
-
|
|
1154
|
-
}, [messages,
|
|
1152
|
+
due.map((message) => announce2 ? announce2({ kind: message.role === "action" ? "action" : "reply", message }) : markdownPlainText(message.text)).forEach((text) => text && announce(text));
|
|
1153
|
+
}, [messages, running2, announce2]);
|
|
1155
1154
|
const updateJumpVisibility = useCallback2((viewport) => {
|
|
1156
1155
|
if (viewport.clientHeight > 0) setShowJumpToEnd(viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop > jumpToEndThreshold);
|
|
1157
1156
|
}, [jumpToEndThreshold]);
|
|
1158
1157
|
useLayoutEffect(() => sendScope?.subscribe(() => {
|
|
1159
|
-
const viewport =
|
|
1158
|
+
const viewport = scrollArea.current;
|
|
1160
1159
|
if (!viewport) return;
|
|
1161
1160
|
scroll.jump(viewport);
|
|
1162
1161
|
updateJumpVisibility(viewport);
|
|
@@ -1169,16 +1168,16 @@ function ChatMessages({
|
|
|
1169
1168
|
}
|
|
1170
1169
|
scroll.pause(viewport);
|
|
1171
1170
|
};
|
|
1172
|
-
const
|
|
1173
|
-
const
|
|
1174
|
-
if (
|
|
1175
|
-
scroll.scroll(
|
|
1176
|
-
updateJumpVisibility(
|
|
1171
|
+
const checkEnd = useCallback2(() => {
|
|
1172
|
+
const area = scrollArea.current;
|
|
1173
|
+
if (area) {
|
|
1174
|
+
scroll.scroll(area, scrollbarDragging.current);
|
|
1175
|
+
updateJumpVisibility(area);
|
|
1177
1176
|
}
|
|
1178
1177
|
}, [scroll, updateJumpVisibility]);
|
|
1179
1178
|
useEffect3(() => {
|
|
1180
1179
|
const finishDrag = () => {
|
|
1181
|
-
if (scrollbarDragging.current &&
|
|
1180
|
+
if (scrollbarDragging.current && scrollArea.current) scroll.scroll(scrollArea.current, true);
|
|
1182
1181
|
scrollbarDragging.current = false;
|
|
1183
1182
|
};
|
|
1184
1183
|
window.addEventListener("pointerup", finishDrag);
|
|
@@ -1189,27 +1188,27 @@ function ChatMessages({
|
|
|
1189
1188
|
};
|
|
1190
1189
|
}, [scroll]);
|
|
1191
1190
|
useLayoutEffect(() => {
|
|
1192
|
-
const
|
|
1193
|
-
if (!
|
|
1191
|
+
const area = scrollArea.current;
|
|
1192
|
+
if (!area) return;
|
|
1194
1193
|
const align = () => {
|
|
1195
|
-
scroll.layout(
|
|
1196
|
-
updateJumpVisibility(
|
|
1194
|
+
scroll.layout(area, scrollbarDragging.current);
|
|
1195
|
+
updateJumpVisibility(area);
|
|
1197
1196
|
};
|
|
1198
1197
|
align();
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
if (threadRef.current)
|
|
1202
|
-
return () =>
|
|
1198
|
+
const observer = new ResizeObserver(align);
|
|
1199
|
+
observer.observe(area);
|
|
1200
|
+
if (threadRef.current) observer.observe(threadRef.current, { box: "border-box" });
|
|
1201
|
+
return () => observer.disconnect();
|
|
1203
1202
|
});
|
|
1204
1203
|
const visible = useMemo3(
|
|
1205
1204
|
() => {
|
|
1206
|
-
const
|
|
1205
|
+
const lastNonUser = messages.reduce((last, message, index) => message.role === "user" ? last : index, -1);
|
|
1207
1206
|
return messages.filter((message, index) => {
|
|
1208
|
-
if (dock !== void 0 &&
|
|
1207
|
+
if (dock !== void 0 && isOpenAction(message)) {
|
|
1209
1208
|
return false;
|
|
1210
1209
|
}
|
|
1211
|
-
if (detailMode === "current" &&
|
|
1212
|
-
return
|
|
1210
|
+
if (detailMode === "current" && isStep(message)) {
|
|
1211
|
+
return running2 && index === lastNonUser && (message.role === "thinking" ? !message.closed : stepState(message) === "running");
|
|
1213
1212
|
}
|
|
1214
1213
|
if ((message.role === "thinking" || message.role === "assistant") && message.text.trim() === "" && !message.attachments?.length) {
|
|
1215
1214
|
return false;
|
|
@@ -1220,54 +1219,54 @@ function ChatMessages({
|
|
|
1220
1219
|
return true;
|
|
1221
1220
|
});
|
|
1222
1221
|
},
|
|
1223
|
-
[messages, detailMode,
|
|
1222
|
+
[messages, detailMode, running2, dock]
|
|
1224
1223
|
);
|
|
1225
|
-
const
|
|
1226
|
-
const
|
|
1224
|
+
const openActions = dock ? messages.filter(isOpenAction) : [];
|
|
1225
|
+
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
1226
|
/* @__PURE__ */ jsx13("time", { dateTime: messageDate(at)?.toISOString(), className: cn(
|
|
1228
1227
|
"qsl:flex-none qsl:text-meta qsl:tabular-nums qsl:text-muted-foreground qsl:opacity-85",
|
|
1229
1228
|
timestampOptions?.format && timestampOptions.format !== "time" ? "qsl:w-[clamp(60px,20%,120px)]" : "qsl:w-[34px]"
|
|
1230
1229
|
), 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
|
|
1230
|
+
/* @__PURE__ */ jsx13("div", { className: "qsl:min-w-0 qsl:flex-1 qsl:[&>*:first-child]:mt-0", children: content })
|
|
1231
|
+
] }, `time-${key}`) : content;
|
|
1232
|
+
const chipMode = detailMode === "current" || detailMode === "chips" || detailMode === "icons";
|
|
1233
|
+
const elapsedAfter = running2 && toolElapsedThreshold !== false ? toolElapsedThreshold : void 0;
|
|
1234
|
+
const groupMode = detailMode === "grouped";
|
|
1235
|
+
const blocks = [];
|
|
1237
1236
|
let lastDay;
|
|
1238
|
-
let
|
|
1239
|
-
const
|
|
1240
|
-
if (
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1237
|
+
let group = [];
|
|
1238
|
+
const closeGroup = () => {
|
|
1239
|
+
if (group.length > 0) {
|
|
1240
|
+
const runningStep = running2 ? [...group].reverse().find((message) => ["running", "thinking"].includes(stepState(message))) : void 0;
|
|
1241
|
+
blocks.push(withTime(
|
|
1242
|
+
groupMode ? /* @__PURE__ */ jsx13(
|
|
1244
1243
|
StepGroup,
|
|
1245
1244
|
{
|
|
1246
1245
|
elapsedAfter,
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
messages:
|
|
1250
|
-
texts:
|
|
1246
|
+
expandable: stepsExpandable,
|
|
1247
|
+
runningStep,
|
|
1248
|
+
messages: group,
|
|
1249
|
+
texts: allTexts,
|
|
1251
1250
|
toolArgumentsText
|
|
1252
1251
|
},
|
|
1253
|
-
|
|
1252
|
+
group[0].key
|
|
1254
1253
|
) : /* @__PURE__ */ jsx13(
|
|
1255
1254
|
StepRow,
|
|
1256
1255
|
{
|
|
1257
1256
|
elapsedAfter,
|
|
1258
|
-
|
|
1259
|
-
messages:
|
|
1260
|
-
|
|
1261
|
-
texts:
|
|
1257
|
+
expandable: stepsExpandable,
|
|
1258
|
+
messages: group,
|
|
1259
|
+
withText: detailMode !== "icons",
|
|
1260
|
+
texts: allTexts,
|
|
1262
1261
|
toolArgumentsText,
|
|
1263
|
-
toolLabel: detailMode === "current" && !stepsExpandable ?
|
|
1262
|
+
toolLabel: detailMode === "current" && !stepsExpandable ? allTexts.currentTool : void 0
|
|
1264
1263
|
},
|
|
1265
|
-
|
|
1264
|
+
group[0].key
|
|
1266
1265
|
),
|
|
1267
|
-
|
|
1268
|
-
|
|
1266
|
+
group[0].key,
|
|
1267
|
+
group[0].at
|
|
1269
1268
|
));
|
|
1270
|
-
|
|
1269
|
+
group = [];
|
|
1271
1270
|
}
|
|
1272
1271
|
};
|
|
1273
1272
|
visible.forEach((message, index) => {
|
|
@@ -1275,38 +1274,38 @@ function ChatMessages({
|
|
|
1275
1274
|
if (date) {
|
|
1276
1275
|
const day = timestampDay(date, timestampOptions);
|
|
1277
1276
|
if (day.key !== lastDay) {
|
|
1278
|
-
|
|
1279
|
-
|
|
1277
|
+
closeGroup();
|
|
1278
|
+
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
1279
|
lastDay = day.key;
|
|
1281
1280
|
}
|
|
1282
1281
|
}
|
|
1283
|
-
const
|
|
1284
|
-
if (
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
/* @__PURE__ */ jsx13("div", { className: stepClasses, children:
|
|
1282
|
+
const customRendering = message.role === "tool" && message.tool && detailMode !== "current" ? renderTool?.(message.tool) : void 0;
|
|
1283
|
+
if (customRendering !== void 0) {
|
|
1284
|
+
closeGroup();
|
|
1285
|
+
blocks.push(withTime(
|
|
1286
|
+
/* @__PURE__ */ jsx13("div", { className: stepClasses, children: customRendering }, message.key),
|
|
1288
1287
|
message.key,
|
|
1289
1288
|
message.at
|
|
1290
1289
|
));
|
|
1291
1290
|
return;
|
|
1292
1291
|
}
|
|
1293
|
-
if ((
|
|
1294
|
-
|
|
1292
|
+
if ((chipMode || groupMode) && isStep(message)) {
|
|
1293
|
+
group = [...group, message];
|
|
1295
1294
|
return;
|
|
1296
1295
|
}
|
|
1297
|
-
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1296
|
+
closeGroup();
|
|
1297
|
+
const dense = isStep(message) && isStep(visible[index - 1]);
|
|
1298
|
+
blocks.push(withTime(
|
|
1300
1299
|
/* @__PURE__ */ jsx13(
|
|
1301
1300
|
Bubble,
|
|
1302
1301
|
{
|
|
1303
1302
|
detailMode,
|
|
1304
|
-
|
|
1303
|
+
dense,
|
|
1305
1304
|
elapsedAfter,
|
|
1306
|
-
|
|
1305
|
+
expandable: stepsExpandable,
|
|
1307
1306
|
message,
|
|
1308
1307
|
plain: owner != null && message.sender === owner,
|
|
1309
|
-
texts:
|
|
1308
|
+
texts: allTexts,
|
|
1310
1309
|
toolArgumentsText,
|
|
1311
1310
|
renderAction,
|
|
1312
1311
|
onDismissAction,
|
|
@@ -1317,11 +1316,11 @@ function ChatMessages({
|
|
|
1317
1316
|
),
|
|
1318
1317
|
message.key,
|
|
1319
1318
|
message.at,
|
|
1320
|
-
|
|
1319
|
+
dense
|
|
1321
1320
|
));
|
|
1322
1321
|
});
|
|
1323
|
-
|
|
1324
|
-
const
|
|
1322
|
+
closeGroup();
|
|
1323
|
+
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
1324
|
...appearanceStyle(appearance),
|
|
1326
1325
|
"--qsl-chat-content-max-width": typeof maxWidth === "number" ? `${maxWidth}px` : maxWidth,
|
|
1327
1326
|
"--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
|
|
@@ -1329,7 +1328,7 @@ function ChatMessages({
|
|
|
1329
1328
|
/* @__PURE__ */ jsx13(
|
|
1330
1329
|
"div",
|
|
1331
1330
|
{
|
|
1332
|
-
"aria-label":
|
|
1331
|
+
"aria-label": allTexts.transcript,
|
|
1333
1332
|
className: cn(
|
|
1334
1333
|
"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
1334
|
"qsl:[--qsl-fade-edge:calc(100%_-_var(--qsl-composer-height,0px))]",
|
|
@@ -1339,7 +1338,7 @@ function ChatMessages({
|
|
|
1339
1338
|
scrollerClassName
|
|
1340
1339
|
),
|
|
1341
1340
|
"data-quassel-transcript": "",
|
|
1342
|
-
onScroll:
|
|
1341
|
+
onScroll: checkEnd,
|
|
1343
1342
|
onWheel: (event) => {
|
|
1344
1343
|
if (!event.ctrlKey && event.deltaY < 0) pauseUp(event.currentTarget, event.target);
|
|
1345
1344
|
},
|
|
@@ -1355,20 +1354,20 @@ function ChatMessages({
|
|
|
1355
1354
|
touchY.current = event.touches[0]?.clientY;
|
|
1356
1355
|
},
|
|
1357
1356
|
onTouchMove: (event) => {
|
|
1358
|
-
const
|
|
1359
|
-
if (
|
|
1360
|
-
touchY.current =
|
|
1357
|
+
const next2 = event.touches[0]?.clientY;
|
|
1358
|
+
if (next2 !== void 0 && touchY.current !== void 0 && next2 > touchY.current) pauseUp(event.currentTarget, event.target);
|
|
1359
|
+
touchY.current = next2;
|
|
1361
1360
|
},
|
|
1362
1361
|
role: "region",
|
|
1363
1362
|
tabIndex: 0,
|
|
1364
1363
|
ref: (element) => {
|
|
1365
|
-
|
|
1364
|
+
scrollArea.current = element;
|
|
1366
1365
|
scrollerRef?.(element);
|
|
1367
1366
|
},
|
|
1368
|
-
children: visible.length === 0 && !
|
|
1367
|
+
children: visible.length === 0 && !running2 ? emptyState ?? null : /* @__PURE__ */ jsxs6(
|
|
1369
1368
|
"div",
|
|
1370
1369
|
{
|
|
1371
|
-
"aria-busy":
|
|
1370
|
+
"aria-busy": running2 || void 0,
|
|
1372
1371
|
className: cn(
|
|
1373
1372
|
"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
1373
|
"qsl:pb-[calc(var(--qsl-composer-height,0px)_+_max(3.25em,40px))]",
|
|
@@ -1376,52 +1375,52 @@ function ChatMessages({
|
|
|
1376
1375
|
),
|
|
1377
1376
|
ref: threadRef,
|
|
1378
1377
|
children: [
|
|
1379
|
-
|
|
1380
|
-
|
|
1378
|
+
blocks,
|
|
1379
|
+
running2 && (working ?? /* @__PURE__ */ jsx13(WorkingScenes, { label: allTexts.working }))
|
|
1381
1380
|
]
|
|
1382
1381
|
}
|
|
1383
1382
|
)
|
|
1384
1383
|
}
|
|
1385
1384
|
),
|
|
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(
|
|
1385
|
+
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: () => {
|
|
1386
|
+
if (scrollArea.current) {
|
|
1387
|
+
scroll.jump(scrollArea.current);
|
|
1388
|
+
updateJumpVisibility(scrollArea.current);
|
|
1390
1389
|
}
|
|
1391
1390
|
}, 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:
|
|
1391
|
+
dock && openActions.length > 0 && createPortal(
|
|
1392
|
+
/* @__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
1393
|
dock
|
|
1395
1394
|
)
|
|
1396
1395
|
] });
|
|
1397
|
-
return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts:
|
|
1396
|
+
return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts: allTexts, children: transcript }) });
|
|
1398
1397
|
}
|
|
1399
1398
|
function StepRow({
|
|
1400
1399
|
messages,
|
|
1401
|
-
|
|
1402
|
-
|
|
1400
|
+
withText,
|
|
1401
|
+
expandable,
|
|
1403
1402
|
elapsedAfter,
|
|
1404
1403
|
texts,
|
|
1405
1404
|
toolArgumentsText,
|
|
1406
1405
|
toolLabel
|
|
1407
1406
|
}) {
|
|
1408
1407
|
const [detail, setDetail] = useState4();
|
|
1409
|
-
const
|
|
1408
|
+
const open = expandable && detail && messages.find((message) => message.key === detail.key);
|
|
1410
1409
|
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
1410
|
messages.map((message) => {
|
|
1412
1411
|
const thinking = message.role === "thinking";
|
|
1413
1412
|
const tool = message.tool;
|
|
1414
1413
|
const state = stepState(message);
|
|
1415
|
-
const
|
|
1414
|
+
const running2 = state === "running" || state === "thinking";
|
|
1416
1415
|
const label = thinking ? texts.thinkingChip : toolLabel ?? (tool?.name || message.text || texts.toolChip);
|
|
1417
1416
|
const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1418
1417
|
thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { size: 11 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? "qsl:text-destructive" : void 0, size: 11 }),
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1418
|
+
withText && /* @__PURE__ */ jsx13("span", { className: "qsl:min-w-0 qsl:self-baseline qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap", children: label }),
|
|
1419
|
+
withText && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, className: "qsl:flex-none qsl:self-baseline", message, texts }),
|
|
1420
|
+
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
1421
|
] });
|
|
1423
|
-
const classes = cn(chipClasses, !
|
|
1424
|
-
if (!
|
|
1422
|
+
const classes = cn(chipClasses, !withText && "qsl:p-1", running2 && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none");
|
|
1423
|
+
if (!expandable) {
|
|
1425
1424
|
return /* @__PURE__ */ jsx13("span", { className: classes, "data-kind": thinking ? "thinking" : "tool", "data-state": state, "data-step": "chip", title: label, children: content }, message.key);
|
|
1426
1425
|
}
|
|
1427
1426
|
return /* @__PURE__ */ jsx13(
|
|
@@ -1440,10 +1439,10 @@ function StepRow({
|
|
|
1440
1439
|
message.key
|
|
1441
1440
|
);
|
|
1442
1441
|
}),
|
|
1443
|
-
|
|
1442
|
+
open && detail && /* @__PURE__ */ jsx13(
|
|
1444
1443
|
StepPopover,
|
|
1445
1444
|
{
|
|
1446
|
-
message:
|
|
1445
|
+
message: open,
|
|
1447
1446
|
position: detail.position,
|
|
1448
1447
|
texts,
|
|
1449
1448
|
toolArgumentsText,
|
|
@@ -1454,58 +1453,58 @@ function StepRow({
|
|
|
1454
1453
|
}
|
|
1455
1454
|
function StepGroup({
|
|
1456
1455
|
messages,
|
|
1457
|
-
|
|
1456
|
+
runningStep,
|
|
1458
1457
|
elapsedAfter,
|
|
1459
|
-
|
|
1458
|
+
expandable,
|
|
1460
1459
|
texts,
|
|
1461
1460
|
toolArgumentsText
|
|
1462
1461
|
}) {
|
|
1463
|
-
const [
|
|
1464
|
-
const
|
|
1465
|
-
const
|
|
1462
|
+
const [open, setOpen] = useState4(false);
|
|
1463
|
+
const header = useRef3(null);
|
|
1464
|
+
const collapsedFromBottom = useRef3(false);
|
|
1466
1465
|
useLayoutEffect(() => {
|
|
1467
|
-
if (!
|
|
1468
|
-
|
|
1469
|
-
|
|
1466
|
+
if (!open && collapsedFromBottom.current) {
|
|
1467
|
+
collapsedFromBottom.current = false;
|
|
1468
|
+
header.current?.scrollIntoView({ block: "nearest" });
|
|
1470
1469
|
}
|
|
1471
|
-
}, [
|
|
1472
|
-
const
|
|
1473
|
-
const
|
|
1474
|
-
const
|
|
1475
|
-
const
|
|
1470
|
+
}, [open]);
|
|
1471
|
+
const active = runningStep !== void 0;
|
|
1472
|
+
const failed = messages.some((message) => message.tool?.isError);
|
|
1473
|
+
const count = messages.length === 1 ? texts.stepGroupOne : texts.stepGroupMany.replace("{count}", String(messages.length));
|
|
1474
|
+
const runningLabel = runningStep && (runningStep.role === "thinking" ? texts.thinkingChip : runningStep.tool?.name || runningStep.text || texts.toolChip);
|
|
1476
1475
|
return /* @__PURE__ */ jsxs6("div", { className: stepClasses, "data-step": "group", children: [
|
|
1477
1476
|
/* @__PURE__ */ jsxs6(
|
|
1478
1477
|
"button",
|
|
1479
1478
|
{
|
|
1480
|
-
"aria-expanded":
|
|
1481
|
-
className: cn(traceClasses, traceButtonClasses, "qsl:items-center qsl:pl-1",
|
|
1482
|
-
onClick: () =>
|
|
1483
|
-
ref:
|
|
1479
|
+
"aria-expanded": open,
|
|
1480
|
+
className: cn(traceClasses, traceButtonClasses, "qsl:items-center qsl:pl-1", active && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none"),
|
|
1481
|
+
onClick: () => setOpen((value) => !value),
|
|
1482
|
+
ref: header,
|
|
1484
1483
|
type: "button",
|
|
1485
1484
|
children: [
|
|
1486
|
-
/* @__PURE__ */ jsx13(ChevronRightIcon, { className: cn("qsl:flex-none qsl:opacity-60 qsl:transition-transform",
|
|
1487
|
-
/* @__PURE__ */ jsx13(LayersIcon, { className:
|
|
1485
|
+
/* @__PURE__ */ jsx13(ChevronRightIcon, { className: cn("qsl:flex-none qsl:opacity-60 qsl:transition-transform", open && "qsl:rotate-90"), size: 12 }),
|
|
1486
|
+
/* @__PURE__ */ jsx13(LayersIcon, { className: failed ? traceErrorIconClasses : traceIconClasses, size: 12 }),
|
|
1488
1487
|
/* @__PURE__ */ jsxs6("span", { className: cn(traceLineClasses, "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap"), children: [
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1488
|
+
count,
|
|
1489
|
+
runningLabel && /* @__PURE__ */ jsxs6("span", { className: traceRunningClasses, children: [
|
|
1490
|
+
runningLabel,
|
|
1492
1491
|
" ",
|
|
1493
1492
|
texts.toolRunning
|
|
1494
1493
|
] }),
|
|
1495
|
-
|
|
1494
|
+
runningStep && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message: runningStep, texts })
|
|
1496
1495
|
] })
|
|
1497
1496
|
]
|
|
1498
1497
|
}
|
|
1499
1498
|
),
|
|
1500
|
-
|
|
1501
|
-
|
|
1499
|
+
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)) }),
|
|
1500
|
+
open && messages.length >= collapseAtBottomFrom && /* @__PURE__ */ jsxs6(
|
|
1502
1501
|
"button",
|
|
1503
1502
|
{
|
|
1504
1503
|
className: cn(traceClasses, traceButtonClasses, "qsl:mt-0.5 qsl:items-center qsl:pl-1"),
|
|
1505
1504
|
"data-step": "group-collapse",
|
|
1506
1505
|
onClick: () => {
|
|
1507
|
-
|
|
1508
|
-
|
|
1506
|
+
collapsedFromBottom.current = true;
|
|
1507
|
+
setOpen(false);
|
|
1509
1508
|
},
|
|
1510
1509
|
type: "button",
|
|
1511
1510
|
children: [
|
|
@@ -1520,7 +1519,7 @@ function TraceLine({
|
|
|
1520
1519
|
message,
|
|
1521
1520
|
className,
|
|
1522
1521
|
elapsedAfter,
|
|
1523
|
-
|
|
1522
|
+
expandable,
|
|
1524
1523
|
texts,
|
|
1525
1524
|
toolArgumentsText
|
|
1526
1525
|
}) {
|
|
@@ -1528,18 +1527,18 @@ function TraceLine({
|
|
|
1528
1527
|
const thinking = message.role === "thinking";
|
|
1529
1528
|
const tool = message.tool;
|
|
1530
1529
|
const classes = cn(traceClasses, className);
|
|
1531
|
-
const
|
|
1532
|
-
const
|
|
1530
|
+
const line = cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap", "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap");
|
|
1531
|
+
const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1533
1532
|
thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { className: traceIconClasses, size: 12 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? traceErrorIconClasses : traceIconClasses, size: 12 }),
|
|
1534
|
-
/* @__PURE__ */ jsxs6("span", { className:
|
|
1533
|
+
/* @__PURE__ */ jsxs6("span", { className: line, children: [
|
|
1535
1534
|
message.text,
|
|
1536
1535
|
tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
|
|
1537
1536
|
/* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
|
|
1538
1537
|
] })
|
|
1539
1538
|
] });
|
|
1540
1539
|
const marks = { "data-compact": "true", "data-kind": thinking ? "thinking" : "tool", "data-step": "line" };
|
|
1541
|
-
if (!
|
|
1542
|
-
return /* @__PURE__ */ jsx13("div", { className: classes, ...marks, children:
|
|
1540
|
+
if (!expandable) {
|
|
1541
|
+
return /* @__PURE__ */ jsx13("div", { className: classes, ...marks, children: content });
|
|
1543
1542
|
}
|
|
1544
1543
|
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1545
1544
|
/* @__PURE__ */ jsx13(
|
|
@@ -1551,7 +1550,7 @@ function TraceLine({
|
|
|
1551
1550
|
...marks,
|
|
1552
1551
|
onClick: (event) => setDetailPosition({ x: event.clientX, y: event.clientY }),
|
|
1553
1552
|
type: "button",
|
|
1554
|
-
children:
|
|
1553
|
+
children: content
|
|
1555
1554
|
}
|
|
1556
1555
|
),
|
|
1557
1556
|
detailPosition && /* @__PURE__ */ jsx13(
|
|
@@ -1579,9 +1578,9 @@ function Bubble({
|
|
|
1579
1578
|
message,
|
|
1580
1579
|
plain,
|
|
1581
1580
|
detailMode,
|
|
1582
|
-
|
|
1581
|
+
dense,
|
|
1583
1582
|
elapsedAfter,
|
|
1584
|
-
|
|
1583
|
+
expandable,
|
|
1585
1584
|
texts,
|
|
1586
1585
|
toolArgumentsText,
|
|
1587
1586
|
renderAction,
|
|
@@ -1589,23 +1588,23 @@ function Bubble({
|
|
|
1589
1588
|
bubbleOptions,
|
|
1590
1589
|
messageActions
|
|
1591
1590
|
}) {
|
|
1592
|
-
const
|
|
1591
|
+
const stepSpacing = dense ? denseStepClasses : stepClasses;
|
|
1593
1592
|
if (message.role === "thinking" || message.role === "tool" && message.tool) {
|
|
1594
1593
|
if (detailMode === "compact") {
|
|
1595
|
-
return /* @__PURE__ */ jsx13(TraceLine, { className:
|
|
1594
|
+
return /* @__PURE__ */ jsx13(TraceLine, { className: stepSpacing, elapsedAfter, expandable, message, texts, toolArgumentsText });
|
|
1596
1595
|
}
|
|
1597
1596
|
const thinking = message.role === "thinking";
|
|
1598
1597
|
const tool = message.tool;
|
|
1599
1598
|
const icon = thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { className: traceIconClasses, size: 12 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? traceErrorIconClasses : traceIconClasses, size: 12 });
|
|
1600
|
-
const
|
|
1599
|
+
const line = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1601
1600
|
message.text,
|
|
1602
1601
|
tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
|
|
1603
1602
|
/* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
|
|
1604
1603
|
] });
|
|
1605
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses,
|
|
1604
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses, stepSpacing), "data-kind": thinking ? "thinking" : "tool", "data-step": "detail", children: [
|
|
1606
1605
|
icon,
|
|
1607
1606
|
/* @__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:
|
|
1607
|
+
/* @__PURE__ */ jsx13("div", { className: cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap"), children: line }),
|
|
1609
1608
|
tool && /* @__PURE__ */ jsxs6("div", { className: "qsl:mt-0.5 qsl:border-l-2 qsl:border-border-soft qsl:pl-2", children: [
|
|
1610
1609
|
/* @__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
1610
|
tool.result !== void 0 && /* @__PURE__ */ jsx13("pre", { className: cn(
|
|
@@ -1617,10 +1616,10 @@ function Bubble({
|
|
|
1617
1616
|
] });
|
|
1618
1617
|
}
|
|
1619
1618
|
if (message.role === "action" && message.action) {
|
|
1620
|
-
return /* @__PURE__ */ jsx13("div", { className:
|
|
1619
|
+
return /* @__PURE__ */ jsx13("div", { className: stepSpacing, children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts }) });
|
|
1621
1620
|
}
|
|
1622
1621
|
if (message.role === "system") {
|
|
1623
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn(
|
|
1622
|
+
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
1623
|
/* @__PURE__ */ jsx13(Markdown, { text: message.text }),
|
|
1625
1624
|
/* @__PURE__ */ jsx13(MessageAttachments, { label: texts.attachments, message })
|
|
1626
1625
|
] });
|
|
@@ -1642,12 +1641,12 @@ function Bubble({
|
|
|
1642
1641
|
/* @__PURE__ */ jsx13(MessageActions, { message, options: messageActions, texts })
|
|
1643
1642
|
] });
|
|
1644
1643
|
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(
|
|
1644
|
+
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
1645
|
return /* @__PURE__ */ jsx13(
|
|
1647
1646
|
"div",
|
|
1648
1647
|
{
|
|
1649
1648
|
"data-chat-text-cursor": cursor,
|
|
1650
|
-
className: cn(
|
|
1649
|
+
className: cn(stepSpacing, "qsl:flex", side === "end" ? "qsl:justify-end" : "qsl:justify-start"),
|
|
1651
1650
|
"data-message": message.role === "user" && !message.bubble && variant === "default" ? "user" : "bubble",
|
|
1652
1651
|
"data-side": side,
|
|
1653
1652
|
children: /* @__PURE__ */ jsx13(
|
|
@@ -1706,8 +1705,8 @@ function ChatPanel({
|
|
|
1706
1705
|
appearance,
|
|
1707
1706
|
scrollOnSend = false
|
|
1708
1707
|
}) {
|
|
1709
|
-
const
|
|
1710
|
-
const
|
|
1708
|
+
const root = useRef4(null);
|
|
1709
|
+
const composerRef = useRef4(null);
|
|
1711
1710
|
const [dock, setDock] = useState5(null);
|
|
1712
1711
|
const hasComposer = composer !== void 0;
|
|
1713
1712
|
const scrollOnSendRef = useRef4(scrollOnSend);
|
|
@@ -1716,23 +1715,23 @@ function ChatPanel({
|
|
|
1716
1715
|
scrollOnSendRef.current = scrollOnSend;
|
|
1717
1716
|
}, [scrollOnSend]);
|
|
1718
1717
|
useEffect4(() => {
|
|
1719
|
-
const element =
|
|
1720
|
-
const
|
|
1721
|
-
if (!element || !
|
|
1718
|
+
const element = composerRef.current;
|
|
1719
|
+
const frame = root.current;
|
|
1720
|
+
if (!element || !frame) {
|
|
1722
1721
|
return;
|
|
1723
1722
|
}
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1726
|
-
|
|
1723
|
+
const measure = () => {
|
|
1724
|
+
frame.style.setProperty("--qsl-composer-height", `${element.offsetHeight}px`);
|
|
1725
|
+
frame.style.setProperty("--qsl-panel-height", `${frame.offsetHeight}px`);
|
|
1727
1726
|
};
|
|
1728
|
-
const
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1727
|
+
const observer = new ResizeObserver(measure);
|
|
1728
|
+
observer.observe(element);
|
|
1729
|
+
observer.observe(frame);
|
|
1730
|
+
measure();
|
|
1732
1731
|
return () => {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1732
|
+
observer.disconnect();
|
|
1733
|
+
frame.style.removeProperty("--qsl-composer-height");
|
|
1734
|
+
frame.style.removeProperty("--qsl-panel-height");
|
|
1736
1735
|
};
|
|
1737
1736
|
}, [hasComposer]);
|
|
1738
1737
|
return /* @__PURE__ */ jsx14(ChatSendContext.Provider, { value: sendScope, children: /* @__PURE__ */ jsx14(ChatActionDock.Provider, { value: hasComposer ? dock : void 0, children: /* @__PURE__ */ jsxs7(
|
|
@@ -1747,7 +1746,7 @@ function ChatPanel({
|
|
|
1747
1746
|
"--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
|
|
1748
1747
|
},
|
|
1749
1748
|
ref: (element) => {
|
|
1750
|
-
|
|
1749
|
+
root.current = element;
|
|
1751
1750
|
if (typeof nodeRef === "function") {
|
|
1752
1751
|
nodeRef(element);
|
|
1753
1752
|
} else if (nodeRef) {
|
|
@@ -1756,7 +1755,7 @@ function ChatPanel({
|
|
|
1756
1755
|
},
|
|
1757
1756
|
children: [
|
|
1758
1757
|
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:
|
|
1758
|
+
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
1759
|
/* @__PURE__ */ jsx14("div", { children: /* @__PURE__ */ jsx14(
|
|
1761
1760
|
"div",
|
|
1762
1761
|
{
|
|
@@ -1834,7 +1833,7 @@ var attachmentMediaClasses = "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondar
|
|
|
1834
1833
|
function ChatInputToolbar({
|
|
1835
1834
|
onSend,
|
|
1836
1835
|
onStop,
|
|
1837
|
-
running = false,
|
|
1836
|
+
running: running2 = false,
|
|
1838
1837
|
disabled = false,
|
|
1839
1838
|
sendDisabled = false,
|
|
1840
1839
|
layout = "card",
|
|
@@ -1859,7 +1858,7 @@ function ChatInputToolbar({
|
|
|
1859
1858
|
onAutoFocusSettled
|
|
1860
1859
|
}) {
|
|
1861
1860
|
const sendScope = useContext4(ChatSendContext);
|
|
1862
|
-
const
|
|
1861
|
+
const allTexts = { ...defaultTexts, ...texts };
|
|
1863
1862
|
const { Button: Button2, StopButton: StopButton2, Toggle: Toggle2 } = useQuasselComponents();
|
|
1864
1863
|
const [draft, setDraft] = useState7(initialValue);
|
|
1865
1864
|
const [sending, setSending] = useState7(false);
|
|
@@ -1883,11 +1882,11 @@ function ChatInputToolbar({
|
|
|
1883
1882
|
const addFiles = (files) => {
|
|
1884
1883
|
if (!files.length) return;
|
|
1885
1884
|
if (disabled || pendingSend.current) {
|
|
1886
|
-
setAttachmentError(
|
|
1885
|
+
setAttachmentError(allTexts.attachmentsWhileBusy);
|
|
1887
1886
|
return;
|
|
1888
1887
|
}
|
|
1889
1888
|
try {
|
|
1890
|
-
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files,
|
|
1889
|
+
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files, allTexts);
|
|
1891
1890
|
} catch (error) {
|
|
1892
1891
|
setAttachmentError(error instanceof Error ? error.message : String(error));
|
|
1893
1892
|
return;
|
|
@@ -1901,17 +1900,17 @@ function ChatInputToolbar({
|
|
|
1901
1900
|
() => updateAttachments((current) => current.map((item) => item.id === entry.id ? {
|
|
1902
1901
|
...entry,
|
|
1903
1902
|
status: "error",
|
|
1904
|
-
error:
|
|
1903
|
+
error: allTexts.attachmentReadFailed
|
|
1905
1904
|
} : item))
|
|
1906
1905
|
);
|
|
1907
1906
|
}
|
|
1908
1907
|
};
|
|
1909
1908
|
const rootRef = useRef6(null);
|
|
1910
|
-
const
|
|
1909
|
+
const textareaRef = useRef6(null);
|
|
1911
1910
|
const autoFocusSettled = useRef6(false);
|
|
1912
1911
|
useEffect5(() => {
|
|
1913
1912
|
if (!autoFocus || autoFocusSettled.current) return;
|
|
1914
|
-
const input2 =
|
|
1913
|
+
const input2 = textareaRef.current;
|
|
1915
1914
|
if (!input2) return;
|
|
1916
1915
|
let frame = 0;
|
|
1917
1916
|
const settle = () => {
|
|
@@ -1960,7 +1959,7 @@ function ChatInputToolbar({
|
|
|
1960
1959
|
disabled: disabled || sending,
|
|
1961
1960
|
onFiles: addFiles,
|
|
1962
1961
|
onPasteText: (text) => {
|
|
1963
|
-
const input2 =
|
|
1962
|
+
const input2 = textareaRef.current;
|
|
1964
1963
|
if (!input2 || disabled || pendingSend.current) return;
|
|
1965
1964
|
const start = input2.selectionStart;
|
|
1966
1965
|
const end = input2.selectionEnd;
|
|
@@ -1997,11 +1996,11 @@ function ChatInputToolbar({
|
|
|
1997
1996
|
insert: (text) => {
|
|
1998
1997
|
pendingFocus.current = true;
|
|
1999
1998
|
changeDraft(text);
|
|
2000
|
-
const element =
|
|
1999
|
+
const element = textareaRef.current;
|
|
2001
2000
|
element?.focus();
|
|
2002
2001
|
if (element?.value === text) element.setSelectionRange(text.length, text.length);
|
|
2003
2002
|
},
|
|
2004
|
-
focus: () =>
|
|
2003
|
+
focus: () => textareaRef.current?.focus(),
|
|
2005
2004
|
submit: (onSubmit, options) => send(onSubmit, options?.allowEmpty),
|
|
2006
2005
|
reset: () => {
|
|
2007
2006
|
generation.current += 1;
|
|
@@ -2024,31 +2023,31 @@ function ChatInputToolbar({
|
|
|
2024
2023
|
return;
|
|
2025
2024
|
}
|
|
2026
2025
|
pendingFocus.current = false;
|
|
2027
|
-
const element =
|
|
2026
|
+
const element = textareaRef.current;
|
|
2028
2027
|
if (element) {
|
|
2029
2028
|
element.focus();
|
|
2030
2029
|
element.setSelectionRange(element.value.length, element.value.length);
|
|
2031
2030
|
}
|
|
2032
2031
|
}, [draft, sending]);
|
|
2033
2032
|
const applyAutoGrow = useCallback3(() => {
|
|
2034
|
-
const element =
|
|
2033
|
+
const element = textareaRef.current;
|
|
2035
2034
|
if (!element || inputMaxRows === void 0) {
|
|
2036
2035
|
return;
|
|
2037
2036
|
}
|
|
2038
|
-
const
|
|
2039
|
-
const
|
|
2040
|
-
const
|
|
2041
|
-
const maximum =
|
|
2037
|
+
const style = getComputedStyle(element);
|
|
2038
|
+
const lineHeight = Number.parseFloat(style.lineHeight) || Number.parseFloat(style.fontSize) * 1.4;
|
|
2039
|
+
const padding = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom);
|
|
2040
|
+
const maximum = lineHeight * Math.max(inputMaxRows, inputRows) + padding;
|
|
2042
2041
|
element.style.height = "auto";
|
|
2043
|
-
const
|
|
2044
|
-
element.style.height = `${Math.min(
|
|
2045
|
-
element.style.overflowY =
|
|
2042
|
+
const contentHeight = element.scrollHeight;
|
|
2043
|
+
element.style.height = `${Math.min(contentHeight, maximum)}px`;
|
|
2044
|
+
element.style.overflowY = contentHeight > maximum ? "auto" : "hidden";
|
|
2046
2045
|
}, [inputMaxRows, inputRows]);
|
|
2047
2046
|
useEffect5(() => {
|
|
2048
2047
|
applyAutoGrow();
|
|
2049
2048
|
}, [draft, applyAutoGrow]);
|
|
2050
2049
|
useEffect5(() => {
|
|
2051
|
-
const element =
|
|
2050
|
+
const element = textareaRef.current;
|
|
2052
2051
|
if (!element || inputMaxRows === void 0) {
|
|
2053
2052
|
return;
|
|
2054
2053
|
}
|
|
@@ -2069,9 +2068,9 @@ function ChatInputToolbar({
|
|
|
2069
2068
|
}
|
|
2070
2069
|
const update = () => setCompact(element.clientWidth < COMPACT_WIDTH_PX);
|
|
2071
2070
|
update();
|
|
2072
|
-
const
|
|
2073
|
-
|
|
2074
|
-
return () =>
|
|
2071
|
+
const observer = new ResizeObserver(update);
|
|
2072
|
+
observer.observe(element);
|
|
2073
|
+
return () => observer.disconnect();
|
|
2075
2074
|
}, []);
|
|
2076
2075
|
const hasAttachments = attachments.length > 0;
|
|
2077
2076
|
useEffect5(() => {
|
|
@@ -2080,7 +2079,7 @@ function ChatInputToolbar({
|
|
|
2080
2079
|
const capabilityError = hasAttachments && attachmentCapabilitiesError ? attachmentCapabilitiesError : attachmentCapabilities ? attachmentCapabilityError(attachments.map((entry) => ({
|
|
2081
2080
|
name: entry.file.name,
|
|
2082
2081
|
mediaType: attachmentMediaType(entry.file)
|
|
2083
|
-
})), attachmentCapabilities,
|
|
2082
|
+
})), attachmentCapabilities, allTexts) : void 0;
|
|
2084
2083
|
const hasContent = draft.trim().length > 0 || attachments.length > 0;
|
|
2085
2084
|
const preparing = attachments.some((entry) => entry.status === "reading");
|
|
2086
2085
|
const attachmentBlocked = attachments.some((entry) => entry.status !== "ready");
|
|
@@ -2099,7 +2098,7 @@ function ChatInputToolbar({
|
|
|
2099
2098
|
const sentRevision = draftRevision.current;
|
|
2100
2099
|
const sentDraft = draft;
|
|
2101
2100
|
const focusOrigin = document.activeElement;
|
|
2102
|
-
const startedFocused = focusOrigin ===
|
|
2101
|
+
const startedFocused = focusOrigin === textareaRef.current || rootRef.current?.contains(focusOrigin);
|
|
2103
2102
|
let leftComposer = false;
|
|
2104
2103
|
const observeFocus = (event) => {
|
|
2105
2104
|
const target = event.target;
|
|
@@ -2130,7 +2129,7 @@ function ChatInputToolbar({
|
|
|
2130
2129
|
document.removeEventListener("pointerdown", observeFocus);
|
|
2131
2130
|
if (generation.current === sentGeneration) {
|
|
2132
2131
|
pendingSend.current = false;
|
|
2133
|
-
pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !==
|
|
2132
|
+
pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !== textareaRef.current) && (document.activeElement === focusOrigin || !toolbar && document.activeElement === document.body);
|
|
2134
2133
|
setSending(false);
|
|
2135
2134
|
}
|
|
2136
2135
|
}
|
|
@@ -2141,28 +2140,28 @@ function ChatInputToolbar({
|
|
|
2141
2140
|
if (!event.repeat) void send();
|
|
2142
2141
|
}
|
|
2143
2142
|
};
|
|
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":
|
|
2143
|
+
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
2144
|
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
2145
|
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
2146
|
/* @__PURE__ */ jsxs8("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:pr-8 qsl:[overflow-wrap:anywhere]", children: [
|
|
2148
2147
|
/* @__PURE__ */ jsx15("span", { className: "qsl:font-medium", title: entry.file.name, children: entry.file.name }),
|
|
2149
2148
|
/* @__PURE__ */ jsx15("small", { children: formatAttachmentSize(entry.file.size) }),
|
|
2150
|
-
entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children:
|
|
2149
|
+
entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children: allTexts.attachmentPreparing }),
|
|
2151
2150
|
entry.status === "error" && /* @__PURE__ */ jsx15("small", { role: "alert", children: entry.error })
|
|
2152
2151
|
] }),
|
|
2153
2152
|
/* @__PURE__ */ jsx15(
|
|
2154
2153
|
Button2,
|
|
2155
2154
|
{
|
|
2156
|
-
"aria-label": fillText(
|
|
2155
|
+
"aria-label": fillText(allTexts.removeAttachment, { name: entry.file.name }),
|
|
2157
2156
|
className: "qsl:absolute qsl:top-1 qsl:right-1",
|
|
2158
2157
|
disabled: disabled || sending,
|
|
2159
2158
|
onClick: () => {
|
|
2160
2159
|
updateAttachments((current) => current.filter((item) => item.id !== entry.id));
|
|
2161
2160
|
setAttachmentError(void 0);
|
|
2162
|
-
|
|
2161
|
+
textareaRef.current?.focus();
|
|
2163
2162
|
},
|
|
2164
2163
|
size: "icon-sm",
|
|
2165
|
-
title: fillText(
|
|
2164
|
+
title: fillText(allTexts.removeAttachment, { name: entry.file.name }),
|
|
2166
2165
|
variant: "outline",
|
|
2167
2166
|
children: /* @__PURE__ */ jsx15(XIcon3, {})
|
|
2168
2167
|
}
|
|
@@ -2174,16 +2173,16 @@ function ChatInputToolbar({
|
|
|
2174
2173
|
sendError && /* @__PURE__ */ jsx15("p", { className: errorClasses, role: "alert", children: sendError }),
|
|
2175
2174
|
failedSubmissions.map((failed) => /* @__PURE__ */ jsxs8("div", { className: cn(errorClasses, "qsl:flex qsl:flex-wrap qsl:items-center qsl:gap-2"), role: "status", children: [
|
|
2176
2175
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2177
|
-
|
|
2176
|
+
allTexts.notSent,
|
|
2178
2177
|
" ",
|
|
2179
2178
|
failed.draft.slice(0, 100),
|
|
2180
2179
|
failed.draft.length > 100 ? " ..." : "",
|
|
2181
|
-
failed.attachments.length > 0 ? ` ${fillText(
|
|
2180
|
+
failed.attachments.length > 0 ? ` ${fillText(allTexts.notSentAttachments, { count: failed.attachments.length })}` : "",
|
|
2182
2181
|
" "
|
|
2183
2182
|
] }),
|
|
2184
2183
|
/* @__PURE__ */ jsx15(Button2, { disabled: disabled || sending, size: "sm", variant: "outline", onClick: () => {
|
|
2185
2184
|
try {
|
|
2186
|
-
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file),
|
|
2185
|
+
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file), allTexts);
|
|
2187
2186
|
} catch (error) {
|
|
2188
2187
|
setAttachmentError(error instanceof Error ? error.message : String(error));
|
|
2189
2188
|
return;
|
|
@@ -2194,22 +2193,22 @@ function ChatInputToolbar({
|
|
|
2194
2193
|
setFailedSubmissions((current) => current.filter((entry) => entry.id !== failed.id));
|
|
2195
2194
|
setAttachmentError(void 0);
|
|
2196
2195
|
pendingFocus.current = true;
|
|
2197
|
-
}, children:
|
|
2196
|
+
}, children: allTexts.insertUnsent })
|
|
2198
2197
|
] }, failed.id))
|
|
2199
2198
|
] });
|
|
2200
2199
|
const sendButtons = /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
2201
|
-
|
|
2202
|
-
(!
|
|
2200
|
+
running2 && onStop && !hasContent && /* @__PURE__ */ jsx15(StopButton2, { className: "qsl:flex-none", label: allTexts.stop, onClick: onStop, size: "icon-sm" }),
|
|
2201
|
+
(!running2 || !onStop || hasContent) && /* @__PURE__ */ jsx15(
|
|
2203
2202
|
Button2,
|
|
2204
2203
|
{
|
|
2205
|
-
"aria-label":
|
|
2204
|
+
"aria-label": allTexts.send,
|
|
2206
2205
|
className: "qsl:flex-none",
|
|
2207
2206
|
disabled: disabled || sendDisabled || sending || attachmentBlocked || !!capabilityError || !hasContent,
|
|
2208
2207
|
onClick: () => {
|
|
2209
2208
|
void send();
|
|
2210
2209
|
},
|
|
2211
2210
|
size: "icon-sm",
|
|
2212
|
-
title:
|
|
2211
|
+
title: running2 ? allTexts.sendIntoRun : allTexts.send,
|
|
2213
2212
|
children: /* @__PURE__ */ jsx15(SendIcon, { size: 14 })
|
|
2214
2213
|
}
|
|
2215
2214
|
)
|
|
@@ -2219,16 +2218,16 @@ function ChatInputToolbar({
|
|
|
2219
2218
|
attachmentsEnabled && /* @__PURE__ */ jsx15(
|
|
2220
2219
|
Button2,
|
|
2221
2220
|
{
|
|
2222
|
-
"aria-label":
|
|
2221
|
+
"aria-label": allTexts.attachFiles,
|
|
2223
2222
|
disabled: disabled || sending,
|
|
2224
2223
|
onClick: () => fileInput.inputRef.current?.click(),
|
|
2225
2224
|
size: "icon-sm",
|
|
2226
|
-
title: fillText(
|
|
2225
|
+
title: fillText(allTexts.attachFilesHint, { count: MAX_CHAT_ATTACHMENTS, size: maxAttachmentSizeLabel }),
|
|
2227
2226
|
variant: "ghost",
|
|
2228
2227
|
children: /* @__PURE__ */ jsx15(PaperclipIcon, {})
|
|
2229
2228
|
}
|
|
2230
2229
|
),
|
|
2231
|
-
preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children:
|
|
2230
|
+
preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children: allTexts.attachmentsPreparing }),
|
|
2232
2231
|
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
2232
|
action.icon,
|
|
2234
2233
|
action.label && /* @__PURE__ */ jsx15("span", { className: action.icon ? "qsl:in-data-[compact=true]:hidden" : void 0, children: action.label })
|
|
@@ -2253,13 +2252,13 @@ function ChatInputToolbar({
|
|
|
2253
2252
|
{
|
|
2254
2253
|
"aria-controls": inputAriaControls,
|
|
2255
2254
|
className: textareaClasses,
|
|
2256
|
-
"aria-label":
|
|
2255
|
+
"aria-label": allTexts.placeholder,
|
|
2257
2256
|
disabled: !toolbar && disabled,
|
|
2258
2257
|
readOnly: toolbar && disabled,
|
|
2259
2258
|
onChange: (event) => changeDraft(event.target.value),
|
|
2260
2259
|
onKeyDown,
|
|
2261
|
-
placeholder:
|
|
2262
|
-
ref:
|
|
2260
|
+
placeholder: running2 ? allTexts.steeringPlaceholder : allTexts.placeholder,
|
|
2261
|
+
ref: textareaRef,
|
|
2263
2262
|
rows: inputRows,
|
|
2264
2263
|
value: draft
|
|
2265
2264
|
}
|
|
@@ -2271,14 +2270,14 @@ function ChatInputToolbar({
|
|
|
2271
2270
|
"data-compact": compact,
|
|
2272
2271
|
"data-layout": layout,
|
|
2273
2272
|
"data-quassel": "",
|
|
2274
|
-
"data-working":
|
|
2273
|
+
"data-working": running2 || sending,
|
|
2275
2274
|
ref: rootRef,
|
|
2276
2275
|
...attachmentsEnabled ? fileInput.dropProps : {},
|
|
2277
2276
|
children: [
|
|
2278
2277
|
attachmentsEnabled && /* @__PURE__ */ jsx15(
|
|
2279
2278
|
"input",
|
|
2280
2279
|
{
|
|
2281
|
-
"aria-label":
|
|
2280
|
+
"aria-label": allTexts.attachFiles,
|
|
2282
2281
|
hidden: true,
|
|
2283
2282
|
multiple: true,
|
|
2284
2283
|
type: "file",
|
|
@@ -2306,8 +2305,8 @@ function ChatInputToolbar({
|
|
|
2306
2305
|
}
|
|
2307
2306
|
|
|
2308
2307
|
// packages/quassel/src/DetailModeSwitch.tsx
|
|
2309
|
-
import {
|
|
2310
|
-
import { jsx as jsx16
|
|
2308
|
+
import { CircleDotIcon, EyeOffIcon, ListCollapseIcon, ListIcon, ShapesIcon, TagsIcon, TextAlignJustifyIcon } from "lucide-react";
|
|
2309
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2311
2310
|
var DETAIL_MODES = ["off", "current", "icons", "chips", "grouped", "compact", "full"];
|
|
2312
2311
|
var TEXT_KEYS = {
|
|
2313
2312
|
off: "detailModeOff",
|
|
@@ -2318,57 +2317,50 @@ var TEXT_KEYS = {
|
|
|
2318
2317
|
compact: "detailModeCompact",
|
|
2319
2318
|
full: "detailModeFull"
|
|
2320
2319
|
};
|
|
2320
|
+
var ICONS = {
|
|
2321
|
+
off: EyeOffIcon,
|
|
2322
|
+
current: CircleDotIcon,
|
|
2323
|
+
icons: ShapesIcon,
|
|
2324
|
+
chips: TagsIcon,
|
|
2325
|
+
grouped: ListCollapseIcon,
|
|
2326
|
+
compact: ListIcon,
|
|
2327
|
+
full: TextAlignJustifyIcon
|
|
2328
|
+
};
|
|
2321
2329
|
var detailModeLabel = (mode, texts) => ({ ...defaultTexts, ...texts })[TEXT_KEYS[mode]];
|
|
2322
2330
|
function DetailModeSwitch({
|
|
2323
2331
|
mode,
|
|
2324
2332
|
onChange,
|
|
2325
2333
|
modes = DETAIL_MODES,
|
|
2326
|
-
collapsible = true,
|
|
2327
2334
|
texts,
|
|
2328
2335
|
className
|
|
2329
2336
|
}) {
|
|
2330
|
-
const
|
|
2337
|
+
const allTexts = { ...defaultTexts, ...texts };
|
|
2331
2338
|
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
|
-
);
|
|
2339
|
+
const current = modes.indexOf(mode);
|
|
2340
|
+
const next2 = modes[(current < 0 ? 0 : current + 1) % modes.length];
|
|
2341
|
+
const Icon = ICONS[mode];
|
|
2342
|
+
const title = `${allTexts.detailModeTitle}: ${detailModeLabel(mode, texts)}`;
|
|
2343
|
+
return /* @__PURE__ */ jsx16(Button2, { "aria-label": title, className, onClick: () => onChange(next2), size: "icon-sm", title, variant: "ghost", children: /* @__PURE__ */ jsx16(Icon, {}) });
|
|
2349
2344
|
}
|
|
2350
2345
|
|
|
2351
2346
|
// packages/quassel/src/TimestampSwitch.tsx
|
|
2352
|
-
import { ClockIcon } from "lucide-react";
|
|
2353
|
-
import { jsx as jsx17
|
|
2354
|
-
function TimestampSwitch({ showTimestamps, onChange,
|
|
2347
|
+
import { ClockFadingIcon, ClockIcon } from "lucide-react";
|
|
2348
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2349
|
+
function TimestampSwitch({ showTimestamps, onChange, texts, className }) {
|
|
2355
2350
|
const labels = { ...defaultTexts, ...texts };
|
|
2356
2351
|
const { Button: Button2 } = useQuasselComponents();
|
|
2357
2352
|
const title = showTimestamps ? labels.hideTimestamps : labels.showTimestamps;
|
|
2358
|
-
return /* @__PURE__ */
|
|
2353
|
+
return /* @__PURE__ */ jsx17(
|
|
2359
2354
|
Button2,
|
|
2360
2355
|
{
|
|
2361
2356
|
"aria-label": title,
|
|
2362
2357
|
"aria-pressed": showTimestamps,
|
|
2363
2358
|
className,
|
|
2364
2359
|
onClick: () => onChange(!showTimestamps),
|
|
2365
|
-
size: "sm",
|
|
2360
|
+
size: "icon-sm",
|
|
2366
2361
|
title,
|
|
2367
2362
|
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
|
-
]
|
|
2363
|
+
children: showTimestamps ? /* @__PURE__ */ jsx17(ClockIcon, {}) : /* @__PURE__ */ jsx17(ClockFadingIcon, {})
|
|
2372
2364
|
}
|
|
2373
2365
|
);
|
|
2374
2366
|
}
|